Skip to main content
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")
    }
})
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)
        }
    }
}
LayoutType Values:
ValueDescription
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)
}
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)
}
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")
}
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()
}
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"
    }
}

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)

Enable/Disable PiP

// 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()

Callbacks Summary

CallbackParameterDescription
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