Monitor layout changes with LayoutListener. This listener provides callbacks for call layout changes, participant list visibility, and Picture-in-Picture (PiP) mode state changes.
Prerequisites
Register Listener
Register a LayoutListener to receive layout event callbacks:
val callSession = CallSession. getInstance ()
callSession. addLayoutListener ( this , object : LayoutListener () {
override fun onCallLayoutChanged (layoutType: LayoutType ) {
Log. d (TAG, "Layout changed to: $layoutType " )
}
override fun onParticipantListVisible () {
Log. d (TAG, "Participant list is now visible" )
}
override fun onParticipantListHidden () {
Log. d (TAG, "Participant list is now hidden" )
}
override fun onPictureInPictureLayoutEnabled () {
Log. d (TAG, "PiP mode enabled" )
}
override fun onPictureInPictureLayoutDisabled () {
Log. d (TAG, "PiP mode disabled" )
}
})
CallSession callSession = CallSession . getInstance ();
callSession . addLayoutListener ( this , new LayoutListener () {
@ Override
public void onCallLayoutChanged ( LayoutType layoutType ) {
Log . d (TAG, "Layout changed to: " + layoutType);
}
@ Override
public void onParticipantListVisible () {
Log . d (TAG, "Participant list is now visible" );
}
@ Override
public void onParticipantListHidden () {
Log . d (TAG, "Participant list is now hidden" );
}
@ Override
public void onPictureInPictureLayoutEnabled () {
Log . d (TAG, "PiP mode enabled" );
}
@ Override
public void onPictureInPictureLayoutDisabled () {
Log . d (TAG, "PiP mode disabled" );
}
});
The listener is automatically removed when the LifecycleOwner (Activity/Fragment) is destroyed, preventing memory leaks.
Callbacks
onCallLayoutChanged
Triggered when the call layout changes between Tile and Spotlight modes.
override fun onCallLayoutChanged (layoutType: LayoutType ) {
Log. d (TAG, "Layout changed to: $layoutType " )
when (layoutType) {
LayoutType.TILE -> {
// Update UI for tile layout
updateLayoutIcon (R.drawable.ic_grid_view)
}
LayoutType.SPOTLIGHT -> {
// Update UI for spotlight layout
updateLayoutIcon (R.drawable.ic_spotlight)
}
}
}
@ Override
public void onCallLayoutChanged ( LayoutType layoutType) {
Log . d (TAG, "Layout changed to: " + layoutType);
switch (layoutType) {
case TILE :
// Update UI for tile layout
updateLayoutIcon ( R . drawable . ic_grid_view );
break ;
case SPOTLIGHT :
// Update UI for spotlight layout
updateLayoutIcon ( R . drawable . ic_spotlight );
break ;
}
}
LayoutType Values:
Value Description TILEGrid layout showing all participants equally SPOTLIGHTFocus on active speaker with others in sidebar
Use Cases:
Update layout toggle button icon
Adjust custom UI overlays
Log layout preference analytics
onParticipantListVisible
Triggered when the participant list panel becomes visible.
override fun onParticipantListVisible () {
Log. d (TAG, "Participant list opened" )
// Track analytics
analytics. logEvent ( "participant_list_opened" )
// Adjust UI if needed
adjustUIForParticipantList (isVisible = true )
}
@ Override
public void onParticipantListVisible () {
Log . d (TAG, "Participant list opened" );
// Track analytics
analytics . logEvent ( "participant_list_opened" );
// Adjust UI if needed
adjustUIForParticipantList ( true );
}
Use Cases:
Log analytics events
Adjust custom UI elements
Pause other UI animations
onParticipantListHidden
Triggered when the participant list panel is hidden.
override fun onParticipantListHidden () {
Log. d (TAG, "Participant list closed" )
// Restore UI
adjustUIForParticipantList (isVisible = false )
}
@ Override
public void onParticipantListHidden () {
Log . d (TAG, "Participant list closed" );
// Restore UI
adjustUIForParticipantList ( false );
}
Use Cases:
Restore UI elements
Resume animations
Update button states
onPictureInPictureLayoutEnabled
Triggered when Picture-in-Picture (PiP) mode is enabled.
override fun onPictureInPictureLayoutEnabled () {
Log. d (TAG, "PiP mode enabled" )
// Hide non-essential UI elements
hideCallControls ()
// Track PiP usage
analytics. logEvent ( "pip_enabled" )
}
@ Override
public void onPictureInPictureLayoutEnabled () {
Log . d (TAG, "PiP mode enabled" );
// Hide non-essential UI elements
hideCallControls ();
// Track PiP usage
analytics . logEvent ( "pip_enabled" );
}
Use Cases:
Hide call control buttons
Simplify UI for small window
Track PiP feature usage
onPictureInPictureLayoutDisabled
Triggered when Picture-in-Picture (PiP) mode is disabled.
override fun onPictureInPictureLayoutDisabled () {
Log. d (TAG, "PiP mode disabled" )
// Restore full UI
showCallControls ()
}
@ Override
public void onPictureInPictureLayoutDisabled () {
Log . d (TAG, "PiP mode disabled" );
// Restore full UI
showCallControls ();
}
Use Cases:
Restore call control buttons
Show full call UI
Resume normal layout
Complete Example
Here’s a complete example handling all layout events:
class CallActivity : AppCompatActivity () {
private lateinit var callSession: CallSession
private lateinit var layoutButton: ImageButton
private lateinit var controlsContainer: View
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView (R.layout.activity_call)
initViews ()
callSession = CallSession. getInstance ()
setupLayoutListener ()
}
private fun initViews () {
layoutButton = findViewById (R.id.layoutButton)
controlsContainer = findViewById (R.id.controlsContainer)
}
private fun setupLayoutListener () {
callSession. addLayoutListener ( this , object : LayoutListener () {
override fun onCallLayoutChanged (layoutType: LayoutType ) {
runOnUiThread {
when (layoutType) {
LayoutType.TILE -> {
layoutButton. setImageResource (R.drawable.ic_grid_view)
layoutButton.contentDescription = "Switch to spotlight"
}
LayoutType.SPOTLIGHT -> {
layoutButton. setImageResource (R.drawable.ic_spotlight)
layoutButton.contentDescription = "Switch to tile"
}
}
}
}
override fun onParticipantListVisible () {
runOnUiThread {
Log. d (TAG, "Participant list visible" )
// Dim background or adjust layout
}
}
override fun onParticipantListHidden () {
runOnUiThread {
Log. d (TAG, "Participant list hidden" )
// Restore normal layout
}
}
override fun onPictureInPictureLayoutEnabled () {
runOnUiThread {
Log. d (TAG, "PiP enabled" )
// Hide controls for PiP mode
controlsContainer.visibility = View.GONE
}
}
override fun onPictureInPictureLayoutDisabled () {
runOnUiThread {
Log. d (TAG, "PiP disabled" )
// Show controls when exiting PiP
controlsContainer.visibility = View.VISIBLE
}
}
})
}
// Enable PiP when user presses home button
override fun onUserLeaveHint () {
super . onUserLeaveHint ()
if (callSession. isSessionActive ()) {
callSession. enablePictureInPictureLayout ()
}
}
companion object {
private const val TAG = "CallActivity"
}
}
public class CallActivity extends AppCompatActivity {
private static final String TAG = "CallActivity" ;
private CallSession callSession ;
private ImageButton layoutButton ;
private View controlsContainer ;
@ Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate (savedInstanceState);
setContentView ( R . layout . activity_call );
initViews ();
callSession = CallSession . getInstance ();
setupLayoutListener ();
}
private void initViews () {
layoutButton = findViewById ( R . id . layoutButton );
controlsContainer = findViewById ( R . id . controlsContainer );
}
private void setupLayoutListener () {
callSession . addLayoutListener ( this , new LayoutListener () {
@ Override
public void onCallLayoutChanged ( LayoutType layoutType ) {
runOnUiThread (() -> {
switch (layoutType) {
case TILE :
layoutButton . setImageResource ( R . drawable . ic_grid_view );
layoutButton . setContentDescription ( "Switch to spotlight" );
break ;
case SPOTLIGHT :
layoutButton . setImageResource ( R . drawable . ic_spotlight );
layoutButton . setContentDescription ( "Switch to tile" );
break ;
}
});
}
@ Override
public void onParticipantListVisible () {
runOnUiThread (() -> {
Log . d (TAG, "Participant list visible" );
// Dim background or adjust layout
});
}
@ Override
public void onParticipantListHidden () {
runOnUiThread (() -> {
Log . d (TAG, "Participant list hidden" );
// Restore normal layout
});
}
@ Override
public void onPictureInPictureLayoutEnabled () {
runOnUiThread (() -> {
Log . d (TAG, "PiP enabled" );
// Hide controls for PiP mode
controlsContainer . setVisibility ( View . GONE );
});
}
@ Override
public void onPictureInPictureLayoutDisabled () {
runOnUiThread (() -> {
Log . d (TAG, "PiP disabled" );
// Show controls when exiting PiP
controlsContainer . setVisibility ( View . VISIBLE );
});
}
});
}
// Enable PiP when user presses home button
@ Override
protected void onUserLeaveHint () {
super . onUserLeaveHint ();
if ( callSession . isSessionActive ()) {
callSession . enablePictureInPictureLayout ();
}
}
}
Controlling Layout Programmatically
You can change the layout and PiP state programmatically:
Change Layout
// Switch to tile layout
callSession. setLayout (LayoutType.TILE)
// Switch to spotlight layout
callSession. setLayout (LayoutType.SPOTLIGHT)
// Switch to tile layout
callSession . setLayout ( LayoutType . TILE );
// Switch to spotlight layout
callSession . setLayout ( LayoutType . SPOTLIGHT );
Enable/Disable PiP
// Enable Picture-in-Picture
callSession. enablePictureInPictureLayout ()
// Disable Picture-in-Picture
callSession. disablePictureInPictureLayout ()
// Enable Picture-in-Picture
callSession . enablePictureInPictureLayout ();
// Disable Picture-in-Picture
callSession . disablePictureInPictureLayout ();
Initial Layout Configuration
Set the initial layout when joining a session:
val sessionSettings = CometChatCalls. SessionSettingsBuilder ()
. setLayout (LayoutType.TILE) // or LayoutType.SPOTLIGHT
. hideChangeLayoutButton ( false ) // Allow users to change layout
. build ()
SessionSettings sessionSettings = new CometChatCalls. SessionSettingsBuilder ()
. setLayout ( LayoutType . TILE ) // or LayoutType.SPOTLIGHT
. hideChangeLayoutButton ( false ) // Allow users to change layout
. build ();
Callbacks Summary
Callback Parameter Description onCallLayoutChangedLayoutTypeCall layout changed (TILE/SPOTLIGHT) onParticipantListVisible- Participant list panel opened onParticipantListHidden- Participant list panel closed onPictureInPictureLayoutEnabled- PiP mode was enabled onPictureInPictureLayoutDisabled- PiP mode was disabled
Next Steps
Layout & UI Control layout programmatically
Session Settings Configure initial layout settings