Skip to main content
The Push Notifications SDK’s calls module turns a CometChat call push into a ringing phone: a call-style notification with Answer and Decline, a full-screen screen over the lock screen, the ringtone, a ring timeout, missed-call notifications and busy handling. It works with the app in the foreground, in the background or not running at all, and needs no Telecom ConnectionService. This page covers the push side of ringing. Starting, answering and ending calls with the Calls SDK is on the Ringing page.

Prerequisites

  • Calls SDK 5.1 set up and the user logged in.
  • A Firebase project with your Android app, google-services.json in the app module, and the Firebase Messaging dependency. The Push SDK never compiles against Firebase; you own it.
  • Push notifications enabled in the CometChat dashboard with an FCM provider, and that provider’s id.
  • minSdk 26, compileSdk 36 and Android Gradle Plugin 8.9.1 or later: the Push Notifications SDK 2.0 depends on androidx.core 1.18, which requires them.

Step 1: Add the dependencies

build.gradle.kts
Adding the calls artifact turns ringing on. The Chat SDK is optional; a calls-only app works.

Step 2: Initialise on every app start

Run this in Application.onCreate, on every app start, before the user logs in. Token registration on login and logout only runs in a process where init ran. The configuration is also saved to disk, so a push that wakes a killed app still rings before your code runs.

Step 3: Delegate from your Firebase service

AndroidManifest.xml
Pass sentTimeMs and hand over every push. Firebase queues pushes while a device dozes; without the sent time a push delivered late looks fresh and rings for a call that is already over. And do not skip call pushes when the app is in the foreground: the push is the only signal that a call is coming in, in every app state.
Send call pushes at high FCM priority. Android lets a background app start a foreground service only with the temporary allowlist that a high-priority data message grants. At normal priority the call still rings, but the process can be killed between the ring and the answer (BFGS denied in logcat).

Step 4: Join from the answer activity

On Answer the SDK stops the ring, asks for the microphone (and the camera for a video call) and starts your answer activity with the call in its extras. Read it and join; there is nothing to “accept”.
IncomingCall gives you sessionId, callerUid, callerName, callerAvatarUrl, isVideo, receiverType and receiverUid. Declare the activity so it can show over the lock screen. Leave the launch mode at its default: the answer intent already carries SINGLE_TOP, and a singleTop launch mode would also stop onNewIntent from starting a fresh screen for a second call.
AndroidManifest.xml
After Answer, the ring becomes a quiet “Connecting call…” notification until the session reports joined; the SDK posts no notification once the call has joined. For a foreground service while the call runs, see Background Handling.

Step 5: Permissions and ring readiness

There is nothing to declare: POST_NOTIFICATIONS, RECORD_AUDIO and CAMERA merge into your manifest from the Calls SDK and the calls module, along with everything else ringing needs. You only request them at runtime. Microphone and camera. The SDK asks when the user answers. A denied microphone joins muted and does not fail the call. Ask earlier as well (at sign-in, for example) so the first call is not where the user meets the prompt, and give your call screen a way to the app’s settings page for a permission denied for good. If your app prompts itself, turn the SDK prompt off with setRequestCallPermissions(false). Ring readiness. Several device settings silence a call with no error anywhere. Check once after sign-in and tell the user:
Call ensureRingReady only after any other permission request of yours has returned; Android drops a request made while another dialog is open.

How a ring ends

The SDK handles every way a ring ends; you write no code for it. A decline sends rejected to the caller (busy if the user is in another call). A 1:1 call nobody answers stops after the ring window (45 s by default), posts a missed call and sends unanswered. For a group call, a decline or a busy only stops the ring on this device: nothing is sent and the call continues for the others; a group call nobody answers here still posts a missed call. A cancel by the caller, or an answer on another device, stops the ring here. A call that arrives during another call rings over it with “Answering ends your current call”. Answering leaves the live call and joins the new one; declining sends busy and the live call continues.

Missed calls

The SDK posts a missed-call notification when a call is not answered here. Tapping it opens the activity from setMissedCallActivity with the call in its extras; without that setting the tap does nothing. Turn them off with setShowMissedCallNotification(false). Call history comes from Call Logs.

When the app is killed

The high-priority push starts the app process and the SDK rings from its saved configuration. Initialise the Calls SDK (and your own SDK setup) in Application.onCreate from stored credentials, not only after a screen opens, so the answer activity finds a ready SDK.

Tokens and logout

You do not register the push token: the SDK registers it on login and unregisters it on logout. Log out of the Calls SDK when the user signs out (CometChatCalls.logout). That stops a ring still playing for the old user and unregisters the token. CometChat.logout alone unregisters the token but does not stop a ring or sign out of the Calls SDK.

Your own incoming-call screen

Set setIncomingCallActivity(...). The SDK still plays the ringtone and handles the timeout and the decline; only the full-screen UI is yours.
Without onIncomingCallScreenOpened, a cancelled call leaves your screen on the lock screen showing an Answer button for a call that no longer exists. Declare the screen so it can show over the lock screen and turn the display on, as the SDK’s own screen is declared:
AndroidManifest.xml

Theming the SDK’s incoming-call screen

The SDK’s incoming-call screen and its call notifications take each colour from, in order:
  1. a CometChatPNIncomingCallTheme set in code;
  2. the cometchat_pn_call_* colour resources, which your app can override by name;
  3. the SDK’s defaults.
Each colour is looked up on its own, so a theme can set only the colours it cares about.

In code

Use this when your colours live in code, differ between light and dark, or change while the app runs.
  • Mode. SYSTEM follows the device’s dark theme. LIGHT and DARK fix the colours, and the resource fallback then resolves in that mode too.
  • Saved. The theme is saved when you pass it, so it also applies when a push starts your app from killed.
  • Kept across init. An init without a theme keeps the saved one. Remove it with setIncomingCallTheme(context, null).
  • Only what you set. The screen applies only the colours the theme sets. If you replaced the layout, your own styling stays for everything else.
CometChatPNIncomingCallTheme the class is not the style resource @style/CometChatPNIncomingCallTheme. The style resource is the screen’s window theme, and can still be overridden by name.

As resources

Override any of these by name in your app’s res/values/colors.xml (and res/values-night/colors.xml for dark). An app’s resource wins over the library’s. Each colour also has a matching setter on CometChatPNIncomingCallTheme.Colors.Builder.
res/values/colors.xml
The strings, drawables and layout of the screen (all prefixed cometchat_pn_) can be overridden the same way. A replacement layout must keep the ids cometchat_pn_initials, cometchat_pn_caller_name, cometchat_pn_call_type, cometchat_pn_answer and cometchat_pn_decline. On Android 12 and later the Answer and Decline buttons in the notification are drawn by the system, so there only the accent colour and the avatar take your colours.

Knowing about a call in your own code

Listen on the Calls SDK, not on this SDK. It is the same callback whether the call arrived by push on a killed process or on a live one:
Ids are keys: the same id registered twice replaces the listener. Remove it when the screen goes away.

Troubleshooting