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 |
|---|
TILE | Grid layout showing all participants equally |
SPOTLIGHT | Focus 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 |
|---|
onCallLayoutChanged | LayoutType | Call 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