> ## 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.

# Migration Guide

> CometChat Calling SDK v5 - Migration Guide for Android

This guide covers migrating from Calls SDK v4 to v5 for Android.

## Drop-in Compatibility

Calls SDK v5 is a **drop-in replacement** for v4. All v4 APIs are preserved as deprecated methods that internally delegate to the new v5 implementations. You can update the dependency version and your existing code will compile and run without changes.

```groovy theme={null}
// Update your dependency version
-implementation "com.cometchat:calls-sdk-android:4.x.x"
+implementation "com.cometchat:calls-sdk-android:5.x.x"
```

<Info>
  If you're using CometChat UI Kits, simply updating the Calls SDK version is sufficient. The UI Kit will continue to work with v5 through the deprecated compatibility layer.
</Info>

## Why Migrate to v5 APIs?

While v4 APIs will continue to work, migrating to v5 APIs gives you:

* **Lifecycle-aware listeners** that auto-cleanup when your Activity/Fragment is destroyed — no more manual `removeCallsEventListeners()` calls
* **Granular event listeners** — 5 focused listener interfaces instead of one monolithic `CometChatCallsEventsListener`
* **`CallSession` singleton** for cleaner session control — all actions on a single object instead of scattered static methods
* **Dedicated `login()` method** — the Calls SDK now handles its own authentication instead of depending on the Chat SDK's auth token or REST APIs
* **Strongly-typed enums** — `AudioMode`, `SessionType`, `LayoutType`, `CameraFacing` instead of raw strings

***

## Initialization

No changes required. The `init()` API is the same in v5.

```java theme={null}
CallAppSettings callAppSettings = new CallAppSettings.CallAppSettingBuilder()
    .setAppId("APP_ID")
    .setRegion("REGION")
    .build();

CometChatCalls.init(context, callAppSettings, new CometChatCalls.CallbackListener<String>() {
    @Override
    public void onSuccess(String s) { }

    @Override
    public void onError(CometChatException e) { }
});
```

***

## Authentication

In v4, the Calls SDK had no dedicated authentication step. It relied on the Chat SDK's auth token (`CometChat.getUserAuthToken()`) or a REST API to obtain an auth token, which you then passed manually to methods like `generateToken(sessionId, authToken, listener)`.

v5 introduces its own `login()` method. After calling `login()`, the SDK caches the auth token internally, so you no longer need to pass it around to other API calls.

<Tabs>
  <Tab title="v4">
    ```java theme={null}
    // No dedicated Calls SDK login — relied on Chat SDK for auth token
    String authToken = CometChat.getUserAuthToken();

    // Had to pass authToken manually to every call that needed it
    CometChatCalls.generateToken(sessionId, authToken, new CometChatCalls.CallbackListener<GenerateToken>() {
        @Override
        public void onSuccess(GenerateToken generateToken) {
            // Use generateToken.getToken() to start a session
        }

        @Override
        public void onError(CometChatException e) { }
    });
    ```
  </Tab>

  <Tab title="v5">
    ```java theme={null}
    // Login to the Calls SDK once (after your Chat SDK login or after obtaining an auth token)
    CometChatCalls.login(authToken, new CometChatCalls.CallbackListener<CallUser>() {
        @Override
        public void onSuccess(CallUser callUser) {
            // Calls SDK is now authenticated — auth token is cached internally
        }

        @Override
        public void onError(CometChatException e) { }
    });
    ```
  </Tab>
</Tabs>

<Note>
  Call `CometChatCalls.login()` once after your user authenticates (e.g., right after `CometChat.login()` succeeds). The SDK stores the auth token internally, so subsequent calls like `generateToken()` and `joinSession()` use it automatically without you having to pass it.
</Note>

***

## Session Settings

`CallSettings` and `CallSettingsBuilder` are replaced by `SessionSettings` and `SessionSettingsBuilder`. The builder methods have been renamed for clarity.

<Tabs>
  <Tab title="v4">
    ```java theme={null}
    CallSettings callSettings = new CometChatCalls.CallSettingsBuilder(activity)
        .setIsAudioOnly(true)
        .setDefaultLayoutEnable(true)
        .showEndCallButton(true)
        .showMuteAudioButton(true)
        .showPauseVideoButton(true)
        .showSwitchCameraButton(true)
        .showAudioModeButton(true)
        .showRecordingButton(true)
        .startWithAudioMuted(false)
        .startWithVideoMuted(false)
        .setDefaultAudioMode("SPEAKER")
        .setMode("DEFAULT")
        .autoRecordOnCallStart(false)
        .build();
    ```
  </Tab>

  <Tab title="v5">
    ```java theme={null}
    SessionSettings sessionSettings = new CometChatCalls.SessionSettingsBuilder()
        .setType(SessionType.AUDIO)
        .startAudioMuted(false)
        .startVideoPaused(false)
        .setLayout(LayoutType.TILE)
        .setAudioMode(AudioMode.SPEAKER)
        .hideLeaveSessionButton(false)
        .hideToggleAudioButton(false)
        .hideToggleVideoButton(false)
        .hideSwitchCameraButton(false)
        .hideAudioModeButton(false)
        .hideRecordingButton(false)
        .hideControlPanel(false)
        .hideHeaderPanel(false)
        .enableAutoStartRecording(false)
        .build();
    ```
  </Tab>
</Tabs>

### Builder Method Mapping

| v4 Method                               | v5 Method                                            | Notes                                   |
| --------------------------------------- | ---------------------------------------------------- | --------------------------------------- |
| `setIsAudioOnly(true)`                  | `setType(SessionType.AUDIO)`                         | Use `SessionType.VIDEO` for video calls |
| `setDefaultLayoutEnable(bool)`          | `hideControlPanel(!bool)` + `hideHeaderPanel(!bool)` | Inverted logic                          |
| `showEndCallButton(bool)`               | `hideLeaveSessionButton(!bool)`                      | Inverted logic                          |
| `showMuteAudioButton(bool)`             | `hideToggleAudioButton(!bool)`                       | Inverted logic                          |
| `showPauseVideoButton(bool)`            | `hideToggleVideoButton(!bool)`                       | Inverted logic                          |
| `showSwitchCameraButton(bool)`          | `hideSwitchCameraButton(!bool)`                      | Inverted logic                          |
| `showAudioModeButton(bool)`             | `hideAudioModeButton(!bool)`                         | Inverted logic                          |
| `showRecordingButton(bool)`             | `hideRecordingButton(!bool)`                         | Inverted logic                          |
| `startWithAudioMuted(bool)`             | `startAudioMuted(bool)`                              | Same logic                              |
| `startWithVideoMuted(bool)`             | `startVideoPaused(bool)`                             | Same logic                              |
| `setDefaultAudioMode("SPEAKER")`        | `setAudioMode(AudioMode.SPEAKER)`                    | Enum instead of string                  |
| `setMode("DEFAULT")`                    | `setLayout(LayoutType.TILE)`                         | Enum instead of string                  |
| `autoRecordOnCallStart(bool)`           | `enableAutoStartRecording(bool)`                     | Same logic                              |
| `setAvatarMode(string)`                 | *Removed*                                            | No longer applicable                    |
| `setMainVideoContainerSetting(setting)` | *Removed*                                            | No longer applicable                    |
| `enableVideoTileDrag(bool)`             | *Removed*                                            | No longer applicable                    |
| `enableVideoTileClick(bool)`            | *Removed*                                            | No longer applicable                    |
| `setEventListener(listener)`            | Use `CallSession` listeners                          | See [Events](#event-listeners) section  |

***

## Joining a Session

`startSession()` is replaced by `joinSession()` which returns a `CallSession` object for controlling the call.

<Tabs>
  <Tab title="v4">
    ```java theme={null}
    // Step 1: Generate token
    CometChatCalls.generateToken(sessionId, authToken, new CometChatCalls.CallbackListener<GenerateToken>() {
        @Override
        public void onSuccess(GenerateToken generateToken) {
            // Step 2: Start session with token string
            CometChatCalls.startSession(generateToken.getToken(), callSettings, callView, 
                new CometChatCalls.CallbackListener<String>() {
                    @Override
                    public void onSuccess(String s) {
                        // Session started
                    }

                    @Override
                    public void onError(CometChatException e) { }
                });
        }

        @Override
        public void onError(CometChatException e) { }
    });
    ```
  </Tab>

  <Tab title="v5">
    ```java theme={null}
    // Option A: Two-step with GenerateToken object
    CometChatCalls.generateToken(sessionId, new CometChatCalls.CallbackListener<GenerateToken>() {
        @Override
        public void onSuccess(GenerateToken generateToken) {
            CometChatCalls.joinSession(generateToken, sessionSettings, callView, 
                new CometChatCalls.CallbackListener<CallSession>() {
                    @Override
                    public void onSuccess(CallSession callSession) {
                        // Use callSession for actions and events
                    }

                    @Override
                    public void onError(CometChatException e) { }
                });
        }

        @Override
        public void onError(CometChatException e) { }
    });

    // Option B: One-step with session ID (recommended)
    CometChatCalls.joinSession(sessionId, sessionSettings, callView, 
        new CometChatCalls.CallbackListener<CallSession>() {
            @Override
            public void onSuccess(CallSession callSession) {
                // Token generation + join handled internally
            }

            @Override
            public void onError(CometChatException e) { }
        });
    ```
  </Tab>
</Tabs>

Key differences:

* v5 `joinSession()` returns a `CallSession` object (not a raw `String`)
* v5 accepts the `GenerateToken` object directly (not just the token string)
* v5 offers a convenience overload that takes `sessionId` directly and handles token generation internally

***

## Session Control (Actions)

In v4, session actions were static methods on `CometChatCalls`. In v5, they're instance methods on `CallSession`.

<Tabs>
  <Tab title="v4">
    ```java theme={null}
    CometChatCalls.endSession();
    CometChatCalls.switchCamera();
    CometChatCalls.muteAudio(true);
    CometChatCalls.pauseVideo(true);
    CometChatCalls.setAudioMode("SPEAKER");
    CometChatCalls.enterPIPMode();
    CometChatCalls.exitPIPMode();
    CometChatCalls.startRecording();
    CometChatCalls.stopRecording();
    ```
  </Tab>

  <Tab title="v5">
    ```java theme={null}
    CallSession callSession = CallSession.getInstance();

    callSession.leaveSession();
    callSession.switchCamera();
    callSession.muteAudio();       // or callSession.unmuteAudio()
    callSession.pauseVideo();      // or callSession.resumeVideo()
    callSession.setAudioMode(AudioMode.SPEAKER);
    callSession.enablePictureInPictureLayout();
    callSession.disablePictureInPictureLayout();
    callSession.startRecording();
    callSession.stopRecording();
    ```
  </Tab>
</Tabs>

### Action Method Mapping

| v4 Static Method                         | v5 CallSession Method                         |
| ---------------------------------------- | --------------------------------------------- |
| `CometChatCalls.endSession()`            | `callSession.leaveSession()`                  |
| `CometChatCalls.switchCamera()`          | `callSession.switchCamera()`                  |
| `CometChatCalls.muteAudio(true)`         | `callSession.muteAudio()`                     |
| `CometChatCalls.muteAudio(false)`        | `callSession.unmuteAudio()`                   |
| `CometChatCalls.pauseVideo(true)`        | `callSession.pauseVideo()`                    |
| `CometChatCalls.pauseVideo(false)`       | `callSession.resumeVideo()`                   |
| `CometChatCalls.setAudioMode("SPEAKER")` | `callSession.setAudioMode(AudioMode.SPEAKER)` |
| `CometChatCalls.enterPIPMode()`          | `callSession.enablePictureInPictureLayout()`  |
| `CometChatCalls.exitPIPMode()`           | `callSession.disablePictureInPictureLayout()` |
| `CometChatCalls.startRecording()`        | `callSession.startRecording()`                |
| `CometChatCalls.stopRecording()`         | `callSession.stopRecording()`                 |
| `CometChatCalls.switchToVideoCall()`     | *Removed*                                     |

***

## Event Listeners

This is the biggest improvement in v5. The single `CometChatCallsEventsListener` is replaced by 5 focused, lifecycle-aware listeners.

### v4: Single Monolithic Listener

```java theme={null}
CometChatCalls.addCallsEventListeners("LISTENER_ID", new CometChatCallsEventsListener() {
    @Override public void onCallEnded() { }
    @Override public void onCallEndButtonPressed() { }
    @Override public void onUserJoined(RTCUser user) { }
    @Override public void onUserLeft(RTCUser user) { }
    @Override public void onUserListChanged(ArrayList<RTCUser> users) { }
    @Override public void onAudioModeChanged(ArrayList<AudioMode> devices) { }
    @Override public void onCallSwitchedToVideo(CallSwitchRequestInfo info) { }
    @Override public void onUserMuted(RTCMutedUser muteObj) { }
    @Override public void onRecordingToggled(RTCRecordingInfo info) { }
    @Override public void onError(CometChatException e) { }
});

// Must manually remove
CometChatCalls.removeCallsEventListeners("LISTENER_ID");
```

### v5: Focused Lifecycle-Aware Listeners

```java theme={null}
CallSession callSession = CallSession.getInstance();

// Session lifecycle events
callSession.addSessionStatusListener(this, new SessionStatusListener() {
    @Override public void onSessionJoined() { }
    @Override public void onSessionLeft() { }
    @Override public void onSessionTimedOut() { }
    @Override public void onConnectionLost() { }
    @Override public void onConnectionRestored() { }
    @Override public void onConnectionClosed() { }
});

// Participant events (replaces onUserJoined, onUserLeft, onUserListChanged, onUserMuted)
callSession.addParticipantEventListener(this, new ParticipantEventListener() {
    @Override public void onParticipantJoined(Participant participant) { }
    @Override public void onParticipantLeft(Participant participant) { }
    @Override public void onParticipantListChanged(List<Participant> participants) { }
    @Override public void onParticipantAudioMuted(Participant participant) { }
    @Override public void onParticipantAudioUnmuted(Participant participant) { }
    @Override public void onParticipantVideoPaused(Participant participant) { }
    @Override public void onParticipantVideoResumed(Participant participant) { }
    @Override public void onParticipantHandRaised(Participant participant) { }
    @Override public void onParticipantHandLowered(Participant participant) { }
    @Override public void onParticipantStartedRecording(Participant participant) { }
    @Override public void onParticipantStoppedRecording(Participant participant) { }
    @Override public void onDominantSpeakerChanged(Participant participant) { }
});

// Media state events (replaces onAudioModeChanged, onRecordingToggled)
callSession.addMediaEventsListener(this, new MediaEventsListener() {
    @Override public void onAudioMuted() { }
    @Override public void onAudioUnMuted() { }
    @Override public void onVideoPaused() { }
    @Override public void onVideoResumed() { }
    @Override public void onRecordingStarted() { }
    @Override public void onRecordingStopped() { }
    @Override public void onAudioModeChanged(AudioMode audioMode) { }
    @Override public void onCameraFacingChanged(CameraFacing facing) { }
});

// Button click events (replaces onCallEndButtonPressed)
callSession.addButtonClickListener(this, new ButtonClickListener() {
    @Override public void onLeaveSessionButtonClicked() { }
    @Override public void onToggleAudioButtonClicked() { }
    @Override public void onToggleVideoButtonClicked() { }
    @Override public void onSwitchCameraButtonClicked() { }
    @Override public void onRaiseHandButtonClicked() { }
    @Override public void onRecordingToggleButtonClicked() { }
});

// Layout events
callSession.addLayoutListener(this, new LayoutListener() {
    @Override public void onCallLayoutChanged(LayoutType layoutType) { }
    @Override public void onPictureInPictureLayoutEnabled() { }
    @Override public void onPictureInPictureLayoutDisabled() { }
});
```

<Note>
  v5 listeners are lifecycle-aware — pass your Activity or Fragment as the first parameter and they'll be automatically removed when the lifecycle owner is destroyed. No need to call `removeCallsEventListeners()`.
</Note>

### Event Mapping

| v4 Event                                       | v5 Listener                | v5 Event                                             |
| ---------------------------------------------- | -------------------------- | ---------------------------------------------------- |
| `onCallEnded()`                                | `SessionStatusListener`    | `onSessionLeft()`                                    |
| `onCallEndButtonPressed()`                     | `ButtonClickListener`      | `onLeaveSessionButtonClicked()`                      |
| `onSessionTimeout()`                           | `SessionStatusListener`    | `onSessionTimedOut()`                                |
| `onUserJoined(RTCUser)`                        | `ParticipantEventListener` | `onParticipantJoined(Participant)`                   |
| `onUserLeft(RTCUser)`                          | `ParticipantEventListener` | `onParticipantLeft(Participant)`                     |
| `onUserListChanged(ArrayList<RTCUser>)`        | `ParticipantEventListener` | `onParticipantListChanged(List<Participant>)`        |
| `onAudioModeChanged(ArrayList<AudioMode>)`     | `MediaEventsListener`      | `onAudioModeChanged(AudioMode)`                      |
| `onCallSwitchedToVideo(CallSwitchRequestInfo)` | *Removed*                  | —                                                    |
| `onUserMuted(RTCMutedUser)`                    | `ParticipantEventListener` | `onParticipantAudioMuted(Participant)`               |
| `onRecordingToggled(RTCRecordingInfo)`         | `MediaEventsListener`      | `onRecordingStarted()` / `onRecordingStopped()`      |
| `onError(CometChatException)`                  | —                          | Errors are returned via `CallbackListener.onError()` |

### New Events in v5

These events are only available with the v5 listener APIs:

| Listener                   | Event                                        | Description                      |
| -------------------------- | -------------------------------------------- | -------------------------------- |
| `SessionStatusListener`    | `onConnectionLost()`                         | Network interrupted              |
| `SessionStatusListener`    | `onConnectionRestored()`                     | Network restored                 |
| `SessionStatusListener`    | `onConnectionClosed()`                       | Connection permanently closed    |
| `ParticipantEventListener` | `onParticipantVideoResumed()`                | Participant turned camera on     |
| `ParticipantEventListener` | `onParticipantVideoPaused()`                 | Participant turned camera off    |
| `ParticipantEventListener` | `onParticipantHandRaised()`                  | Participant raised hand          |
| `ParticipantEventListener` | `onParticipantHandLowered()`                 | Participant lowered hand         |
| `ParticipantEventListener` | `onParticipantStartedScreenShare()`          | Participant started screen share |
| `ParticipantEventListener` | `onParticipantStoppedScreenShare()`          | Participant stopped screen share |
| `ParticipantEventListener` | `onDominantSpeakerChanged()`                 | Active speaker changed           |
| `MediaEventsListener`      | `onCameraFacingChanged()`                    | Camera switched front/back       |
| `ButtonClickListener`      | Various button events                        | Individual button click tracking |
| `LayoutListener`           | `onCallLayoutChanged()`                      | Layout type changed              |
| `LayoutListener`           | `onPictureInPictureLayoutEnabled/Disabled()` | PiP state changed                |

***

***

## Call Logs

The `CallLogRequest` API is unchanged. The only deprecated method is `setAuthToken()` — in v5, auth is handled by `CometChatCalls.login()`.

<Tabs>
  <Tab title="v4">
    ```java theme={null}
    CallLogRequest request = new CallLogRequest.CallLogRequestBuilder()
        .setAuthToken(authToken)
        .setLimit(30)
        .build();
    ```
  </Tab>

  <Tab title="v5">
    ```java theme={null}
    // Auth token is handled by CometChatCalls.login()
    CallLogRequest request = new CallLogRequest.CallLogRequestBuilder()
        .setLimit(30)
        .build();
    ```
  </Tab>
</Tabs>

***

## Deprecated Classes Summary

These classes still exist in v5 for backward compatibility but are deprecated:

| Deprecated Class                     | Replacement                             |
| ------------------------------------ | --------------------------------------- |
| `CallSettings`                       | `SessionSettings`                       |
| `CometChatCalls.CallSettingsBuilder` | `CometChatCalls.SessionSettingsBuilder` |
| `CometChatCallsEventsListener`       | 5 focused listeners on `CallSession`    |
| `RTCRecordingInfo`                   | `MediaEventsListener` callbacks         |
| `CallSwitchRequestInfo`              | *Removed (no replacement)*              |
