> ## Documentation Index
> Fetch the complete documentation index at: https://www.cometchat.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Control Panel

> CometChat Calling SDK v5 - Custom Control Panel for Android

Build a fully customized control panel for your call interface by hiding the default controls and implementing your own UI with call actions. This guide walks you through creating a custom control panel with essential call controls.

## Overview

Custom control panels allow you to:

* Match your app's branding and design language
* Simplify the interface by showing only relevant controls
* Add custom functionality and workflows
* Create unique user experiences

This guide demonstrates building a basic custom control panel with:

* Mute/Unmute audio button
* Pause/Resume video button
* Switch camera button
* End call button

## Prerequisites

* CometChat Calls SDK installed and initialized
* Active call session (see [Join Session](/calls/android/join-session))
* Familiarity with [Actions](/calls/android/actions) and [Events](/calls/android/events)

***

## Step 1: Hide Default Controls

Configure your session settings to hide the default control panel:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    val sessionSettings = CometChatCalls.SessionSettingsBuilder()
        .hideControlPanel(true)
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
        .hideControlPanel(true)
        .build();
    ```
  </Tab>
</Tabs>

<Note>
  You can also hide individual buttons while keeping the control panel visible. See [SessionSettingsBuilder](/calls/android/session-settings) for all options.
</Note>

***

## Step 2: Create Custom Layout

Create an XML layout for your custom controls:

<Accordion title="activity_call.xml">
  ```xml theme={null}
  <?xml version="1.0" encoding="utf-8"?>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <!-- Call View Container -->
      <FrameLayout
          android:id="@+id/callContainer"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

      <!-- Custom Control Panel -->
      <LinearLayout
          android:id="@+id/customControlPanel"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true"
          android:orientation="horizontal"
          android:gravity="center"
          android:padding="16dp"
          android:background="#CC000000">

          <!-- Mute/Unmute Button -->
          <ImageButton
              android:id="@+id/btnToggleAudio"
              android:layout_width="56dp"
              android:layout_height="56dp"
              android:layout_margin="8dp"
              android:src="@drawable/ic_mic_on"
              android:background="@drawable/control_button_background"
              android:contentDescription="Toggle Audio" />

          <!-- Pause/Resume Video Button -->
          <ImageButton
              android:id="@+id/btnToggleVideo"
              android:layout_width="56dp"
              android:layout_height="56dp"
              android:layout_margin="8dp"
              android:src="@drawable/ic_video_on"
              android:background="@drawable/control_button_background"
              android:contentDescription="Toggle Video" />

          <!-- Switch Camera Button -->
          <ImageButton
              android:id="@+id/btnSwitchCamera"
              android:layout_width="56dp"
              android:layout_height="56dp"
              android:layout_margin="8dp"
              android:src="@drawable/ic_switch_camera"
              android:background="@drawable/control_button_background"
              android:contentDescription="Switch Camera" />

          <!-- End Call Button -->
          <ImageButton
              android:id="@+id/btnEndCall"
              android:layout_width="56dp"
              android:layout_height="56dp"
              android:layout_margin="8dp"
              android:src="@drawable/ic_call_end"
              android:background="@drawable/end_call_button_background"
              android:contentDescription="End Call" />

      </LinearLayout>
  </RelativeLayout>
  ```
</Accordion>

<Accordion title="Button Background Drawables">
  **control\_button\_background.xml:**

  ```xml theme={null}
  <?xml version="1.0" encoding="utf-8"?>
  <shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="oval">
      <solid android:color="#4D4D4D" />
  </shape>
  ```

  **end\_call\_button\_background.xml:**

  ```xml theme={null}
  <?xml version="1.0" encoding="utf-8"?>
  <shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="oval">
      <solid android:color="#FF3B30" />
  </shape>
  ```
</Accordion>

***

## Step 3: Implement Control Actions

Set up button click listeners and call the appropriate actions:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    class CallActivity : AppCompatActivity() {
        
        private lateinit var callSession: CallSession
        private var isAudioMuted = false
        private var isVideoPaused = false
        
        private lateinit var btnToggleAudio: ImageButton
        private lateinit var btnToggleVideo: ImageButton
        private lateinit var btnSwitchCamera: ImageButton
        private lateinit var btnEndCall: ImageButton
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_call)
            
            callSession = CallSession.getInstance()
            
            btnToggleAudio = findViewById(R.id.btnToggleAudio)
            btnToggleVideo = findViewById(R.id.btnToggleVideo)
            btnSwitchCamera = findViewById(R.id.btnSwitchCamera)
            btnEndCall = findViewById(R.id.btnEndCall)
            
            setupControlListeners()
        }
        
        private fun setupControlListeners() {
            btnToggleAudio.setOnClickListener {
                if (isAudioMuted) {
                    callSession.unMuteAudio()
                } else {
                    callSession.muteAudio()
                }
            }
            
            btnToggleVideo.setOnClickListener {
                if (isVideoPaused) {
                    callSession.resumeVideo()
                } else {
                    callSession.pauseVideo()
                }
            }
            
            btnSwitchCamera.setOnClickListener {
                callSession.switchCamera()
            }
            
            btnEndCall.setOnClickListener {
                callSession.leaveSession()
                finish()
            }
        }
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    public class CallActivity extends AppCompatActivity {
        
        private CallSession callSession;
        private boolean isAudioMuted = false;
        private boolean isVideoPaused = false;
        
        private ImageButton btnToggleAudio;
        private ImageButton btnToggleVideo;
        private ImageButton btnSwitchCamera;
        private ImageButton btnEndCall;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_call);
            
            callSession = CallSession.getInstance();
            
            btnToggleAudio = findViewById(R.id.btnToggleAudio);
            btnToggleVideo = findViewById(R.id.btnToggleVideo);
            btnSwitchCamera = findViewById(R.id.btnSwitchCamera);
            btnEndCall = findViewById(R.id.btnEndCall);
            
            setupControlListeners();
        }
        
        private void setupControlListeners() {
            btnToggleAudio.setOnClickListener(v -> {
                if (isAudioMuted) {
                    callSession.unMuteAudio();
                } else {
                    callSession.muteAudio();
                }
            });
            
            btnToggleVideo.setOnClickListener(v -> {
                if (isVideoPaused) {
                    callSession.resumeVideo();
                } else {
                    callSession.pauseVideo();
                }
            });
            
            btnSwitchCamera.setOnClickListener(v -> callSession.switchCamera());
            
            btnEndCall.setOnClickListener(v -> {
                callSession.leaveSession();
                finish();
            });
        }
    }
    ```
  </Tab>
</Tabs>

***

## Step 4: Handle State Updates

Use `MediaEventsListener` to keep your UI synchronized with the actual call state. The listener is lifecycle-aware and automatically removed when the Activity is destroyed.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    private fun setupMediaEventsListener() {
        callSession.addMediaEventsListener(this, object : MediaEventsListener() {
            override fun onAudioMuted() {
                runOnUiThread {
                    isAudioMuted = true
                    btnToggleAudio.setImageResource(R.drawable.ic_mic_off)
                }
            }

            override fun onAudioUnMuted() {
                runOnUiThread {
                    isAudioMuted = false
                    btnToggleAudio.setImageResource(R.drawable.ic_mic_on)
                }
            }

            override fun onVideoPaused() {
                runOnUiThread {
                    isVideoPaused = true
                    btnToggleVideo.setImageResource(R.drawable.ic_video_off)
                }
            }

            override fun onVideoResumed() {
                runOnUiThread {
                    isVideoPaused = false
                    btnToggleVideo.setImageResource(R.drawable.ic_video_on)
                }
            }
        })
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    private void setupMediaEventsListener() {
        callSession.addMediaEventsListener(this, new MediaEventsListener() {
            @Override
            public void onAudioMuted() {
                runOnUiThread(() -> {
                    isAudioMuted = true;
                    btnToggleAudio.setImageResource(R.drawable.ic_mic_off);
                });
            }

            @Override
            public void onAudioUnMuted() {
                runOnUiThread(() -> {
                    isAudioMuted = false;
                    btnToggleAudio.setImageResource(R.drawable.ic_mic_on);
                });
            }

            @Override
            public void onVideoPaused() {
                runOnUiThread(() -> {
                    isVideoPaused = true;
                    btnToggleVideo.setImageResource(R.drawable.ic_video_off);
                });
            }

            @Override
            public void onVideoResumed() {
                runOnUiThread(() -> {
                    isVideoPaused = false;
                    btnToggleVideo.setImageResource(R.drawable.ic_video_on);
                });
            }
        });
    }
    ```
  </Tab>
</Tabs>

Use `SessionStatusListener` to handle session end events:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    private fun setupSessionStatusListener() {
        callSession.addSessionStatusListener(this, object : SessionStatusListener() {
            override fun onSessionLeft() {
                runOnUiThread { finish() }
            }

            override fun onConnectionClosed() {
                runOnUiThread { finish() }
            }
        })
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    private void setupSessionStatusListener() {
        callSession.addSessionStatusListener(this, new SessionStatusListener() {
            @Override
            public void onSessionLeft() {
                runOnUiThread(() -> finish());
            }

            @Override
            public void onConnectionClosed() {
                runOnUiThread(() -> finish());
            }
        });
    }
    ```
  </Tab>
</Tabs>

***

## Complete Example

Here's the full implementation combining all steps:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    class CallActivity : AppCompatActivity() {
        
        private lateinit var callSession: CallSession
        private var isAudioMuted = false
        private var isVideoPaused = false
        
        private lateinit var btnToggleAudio: ImageButton
        private lateinit var btnToggleVideo: ImageButton
        private lateinit var btnSwitchCamera: ImageButton
        private lateinit var btnEndCall: ImageButton
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_call)
            
            callSession = CallSession.getInstance()
            
            btnToggleAudio = findViewById(R.id.btnToggleAudio)
            btnToggleVideo = findViewById(R.id.btnToggleVideo)
            btnSwitchCamera = findViewById(R.id.btnSwitchCamera)
            btnEndCall = findViewById(R.id.btnEndCall)
            
            setupControlListeners()
            setupMediaEventsListener()
            setupSessionStatusListener()
            joinCall()
        }
        
        private fun joinCall() {
            val sessionSettings = CometChatCalls.SessionSettingsBuilder()
                .setDisplayName("John Doe")
                .setType(SessionType.VIDEO)
                .hideControlPanel(true)
                .build()

            val callContainer = findViewById<FrameLayout>(R.id.callContainer)

            CometChatCalls.joinSession(
                sessionId = "SESSION_ID",
                sessionSettings = sessionSettings,
                view = callContainer,
                context = this,
                listener = object : CometChatCalls.CallbackListener<Void>() {
                    override fun onSuccess(p0: Void?) {
                        Log.d(TAG, "Joined call successfully")
                    }

                    override fun onError(exception: CometChatException) {
                        Log.e(TAG, "Failed to join: ${exception.message}")
                        finish()
                    }
                }
            )
        }
        
        private fun setupControlListeners() {
            btnToggleAudio.setOnClickListener {
                if (isAudioMuted) callSession.unMuteAudio() 
                else callSession.muteAudio()
            }
            
            btnToggleVideo.setOnClickListener {
                if (isVideoPaused) callSession.resumeVideo() 
                else callSession.pauseVideo()
            }
            
            btnSwitchCamera.setOnClickListener {
                callSession.switchCamera()
            }
            
            btnEndCall.setOnClickListener {
                callSession.leaveSession()
                finish()
            }
        }
        
        private fun setupMediaEventsListener() {
            callSession.addMediaEventsListener(this, object : MediaEventsListener() {
                override fun onAudioMuted() {
                    runOnUiThread {
                        isAudioMuted = true
                        btnToggleAudio.setImageResource(R.drawable.ic_mic_off)
                    }
                }

                override fun onAudioUnMuted() {
                    runOnUiThread {
                        isAudioMuted = false
                        btnToggleAudio.setImageResource(R.drawable.ic_mic_on)
                    }
                }

                override fun onVideoPaused() {
                    runOnUiThread {
                        isVideoPaused = true
                        btnToggleVideo.setImageResource(R.drawable.ic_video_off)
                    }
                }

                override fun onVideoResumed() {
                    runOnUiThread {
                        isVideoPaused = false
                        btnToggleVideo.setImageResource(R.drawable.ic_video_on)
                    }
                }
            })
        }
        
        private fun setupSessionStatusListener() {
            callSession.addSessionStatusListener(this, object : SessionStatusListener() {
                override fun onSessionLeft() {
                    runOnUiThread { finish() }
                }

                override fun onConnectionClosed() {
                    runOnUiThread { finish() }
                }
            })
        }
        
        companion object {
            private const val TAG = "CallActivity"
        }
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    public class CallActivity extends AppCompatActivity {
        
        private static final String TAG = "CallActivity";
        
        private CallSession callSession;
        private boolean isAudioMuted = false;
        private boolean isVideoPaused = false;
        
        private ImageButton btnToggleAudio;
        private ImageButton btnToggleVideo;
        private ImageButton btnSwitchCamera;
        private ImageButton btnEndCall;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_call);
            
            callSession = CallSession.getInstance();
            
            btnToggleAudio = findViewById(R.id.btnToggleAudio);
            btnToggleVideo = findViewById(R.id.btnToggleVideo);
            btnSwitchCamera = findViewById(R.id.btnSwitchCamera);
            btnEndCall = findViewById(R.id.btnEndCall);
            
            setupControlListeners();
            setupMediaEventsListener();
            setupSessionStatusListener();
            joinCall();
        }
        
        private void joinCall() {
            SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
                .setDisplayName("John Doe")
                .setType(SessionType.VIDEO)
                .hideControlPanel(true)
                .build();

            FrameLayout callContainer = findViewById(R.id.callContainer);

            CometChatCalls.joinSession(
                "SESSION_ID",
                sessionSettings,
                callContainer,
                this,
                new CometChatCalls.CallbackListener<Void>() {
                    @Override
                    public void onSuccess(Void unused) {
                        Log.d(TAG, "Joined call successfully");
                    }

                    @Override
                    public void onError(CometChatException e) {
                        Log.e(TAG, "Failed to join: " + e.getMessage());
                        finish();
                    }
                }
            );
        }
        
        private void setupControlListeners() {
            btnToggleAudio.setOnClickListener(v -> {
                if (isAudioMuted) callSession.unMuteAudio();
                else callSession.muteAudio();
            });
            
            btnToggleVideo.setOnClickListener(v -> {
                if (isVideoPaused) callSession.resumeVideo();
                else callSession.pauseVideo();
            });
            
            btnSwitchCamera.setOnClickListener(v -> callSession.switchCamera());
            
            btnEndCall.setOnClickListener(v -> {
                callSession.leaveSession();
                finish();
            });
        }
        
        private void setupMediaEventsListener() {
            callSession.addMediaEventsListener(this, new MediaEventsListener() {
                @Override
                public void onAudioMuted() {
                    runOnUiThread(() -> {
                        isAudioMuted = true;
                        btnToggleAudio.setImageResource(R.drawable.ic_mic_off);
                    });
                }

                @Override
                public void onAudioUnMuted() {
                    runOnUiThread(() -> {
                        isAudioMuted = false;
                        btnToggleAudio.setImageResource(R.drawable.ic_mic_on);
                    });
                }

                @Override
                public void onVideoPaused() {
                    runOnUiThread(() -> {
                        isVideoPaused = true;
                        btnToggleVideo.setImageResource(R.drawable.ic_video_off);
                    });
                }

                @Override
                public void onVideoResumed() {
                    runOnUiThread(() -> {
                        isVideoPaused = false;
                        btnToggleVideo.setImageResource(R.drawable.ic_video_on);
                    });
                }
            });
        }
        
        private void setupSessionStatusListener() {
            callSession.addSessionStatusListener(this, new SessionStatusListener() {
                @Override
                public void onSessionLeft() {
                    runOnUiThread(() -> finish());
                }

                @Override
                public void onConnectionClosed() {
                    runOnUiThread(() -> finish());
                }
            });
        }
    }
    ```
  </Tab>
</Tabs>
