Picture-in-Picture Mode - Android
Overview​
Picture-in-Picture (PiP) mode enables users to continue video calls while interacting with other apps. This native Android feature is particularly valuable for video conferencing applications, allowing seamless multitasking. Our implementation uses Android's built-in PiP API without third-party dependencies.
This guide will help you integrate PiP mode into your app, ensuring it activates both when the user presses the PiP button and when the app is sent to the background.

You can check out the complete implementation in the Github repository.
Prerequisites​
- Clone our Quick Start Repository
- Run the base application
- Ensure Android API level 26+ (Oreo) for PiP support
Android Manifest Setup​
Add these essential attributes to your AndroidManifest.xml
:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:resizeableActivity="true"
android:supportsPictureInPicture="true">
</activity>
Getting Started with the Code!​
ViewModel​
The MeetingViewModel
manages two key variables: pipMode
and meetingJoined
.
-
pipMode
tracks whether the app is currently in Picture-in-Picture (PiP) mode. This variable is primarily used in the UI to show or hide relevant controls based on the PiP state. -
meetingJoined
indicates whether the user is currently in a meeting. This is useful in various scenarios, such as determining whether to allow PiP mode whenonPictureInPictureRequested
is called.
class MeetingViewModel : ViewModel() {
// Track PiP activation state
var pipMode = mutableStateOf(false)
// Monitor meeting connection status
var meetingJoined = mutableStateOf(false)
private val meetingEventListener: MeetingEventListener = object : MeetingEventListener() {
override fun onMeetingJoined() {
meetingJoined.value = true
}
override fun onMeetingLeft() {
meetingJoined.value = false
}
}
}
MainActivity​
- Handling PiP Mode Changes (
onPictureInPictureModeChanged
)
- This method is triggered whenever the activity enters or exits PiP mode (e.g., when the user minimizes the app or returns to full-screen mode).
- The system passes a
Boolean
flag (isInPictureInPictureMode
), which indicates whether the activity is currently in PiP mode. - Here, we update the
pipMode
state inMeetingViewModel
to notify the UI.
- Handling PiP Mode Requests (
onPictureInPictureRequested
)
- This method is called when the system suggests entering PiP mode, typically when the user presses the home button or switches to another app.
- It checks whether the user is in a meeting (
meetingJoined == true
) before allowing PiP mode.
class MainActivity : ComponentActivity() {
// ViewModel instance for managing meeting state
private lateinit var viewModel: MeetingViewModel
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the ViewModel using ViewModelProvider
viewModel = ViewModelProvider(this)[MeetingViewModel::class.java]
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
// Update the ViewModel state when PiP mode changes
viewModel.pipMode.value = isInPictureInPictureMode
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
}
override fun onPictureInPictureRequested(): Boolean {
// Allow PiP mode only if the meeting is active
viewModel.pipMode.value = viewModel.meetingJoined.value
return super.onPictureInPictureRequested()
}
}
MeetingScreen​
The MeetingScreen
UI includes a Picture-in-Picture (PiP) mode button, allowing users to switch to PiP mode when a meeting is joined. The screen displays meeting participants in a grid layout. If the user is alone, a message is shown. If there are more than two participants, only the first two are displayed.
-
When not in PiP mode, the full meeting UI is shown, including the header, participant grid, and media control buttons (Join, Mic, Cam, Leave, PiP).
-
When in PiP mode, the UI adapts by hiding the header and buttons. If the user is alone, a message is displayed. Otherwise, up to two participants are shown.
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun MeetingScreen(
viewModel: MeetingViewModel,
navController: NavController,
meetingId: String,
context: Context
) {
// Observing the Picture-in-Picture (PiP) mode state from the ViewModel
val pipMode = viewModel.pipMode
// If PiP mode is not enabled, show the full meeting UI
if (!pipMode.value) {
Column(modifier = Modifier.fillMaxSize()) {
Header(meetingId)
MySpacer()
ParticipantsGrid(
gridCells = GridCells.Fixed(2),
participants = viewModel.participants,
modifier = Modifier.weight(1f)
)
MySpacer()
// Media control buttons (Join, Mic, Cam, Leave, PiP)
MediaControlButtons(
onJoinClick = { viewModel.initMeeting(
context, app.sampleToken, meetingId
) },
onMicClick = { viewModel.toggleMic() },
onCamClick = { viewModel.toggleWebcam() },
onLeaveClick = { viewModel.leaveMeeting() },
// Enable PiP if meeting is joined
onPiPClick = { pipMode.value = viewModel.meetingJoined.value }
)
}
}
// If PiP mode is enabled, adjust UI accordingly
if (pipMode.value) {
activity?.enterPictureInPictureMode(
PictureInPictureParams.Builder()
// Custom aspect ratio for PiP window
.setAspectRatio(Rational(16, 9)) .build()
)
if (viewModel.participants.size == 1) {
Row(modifier = Modifier.fillMaxSize()) {
// Display the single participant
Box(modifier = Modifier.weight(1f)) {
ParticipantsGrid(
gridCells = GridCells.Fixed(1),
participants = viewModel.participants.take(1),
)
}
// Display a message when the user is alone in the meeting
Box(
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.wrapContentSize(Alignment.Center),
contentAlignment = Alignment.Center
) {
Text(
text = "You are alone \n in the \n meeting",
color = Color.Black,
textAlign = TextAlign.Center,
fontSize = 12.sp,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
}
}
} else {
// Display up to two participants in PiP mode
ParticipantsGrid(
gridCells = GridCells.Fixed(2),
participants = viewModel.participants.take(2),
modifier = Modifier.fillMaxSize()
)
}
}
}
@Composable
fun MediaControlButtons(
// .....
onPiPClick: () -> Unit
) {
Row {
// Other buttons (Join, Mic, Cam, Leave) can be placed here
// Button to toggle Picture-in-Picture mode
MyAppButton(onPiPClick, "PiP Mode")
}
}
Final Output​
The final output of Picture-in-Picture (PiP) Mode in the Android application using VideoSDK, without relying on any third-party packages. You can further customize the view and optimize it as per your requirements.
Stuck anywhere? Check out this example code on GitHub
Got a Question? Ask us on discord