CallKit for Android in React Native

Recently we’ve encountered a new challenge: add a custom screen for a push notification in an app written in React Native to receive VoIP calls like Facebook Messenger, Whatsapp and Skype are doing.

Have you ever used React Native? It’s a framework developed by Facebook that allows you to build native apps with Javascript!

React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components.

The first step of the call is a notification. We’re using OneSignal to send push notifications to the device, and there is a npm package for React Native ( to manage that. Our goal was to build an app to make VoIP calls.

Users are used to receive the call even if the device is locked and the app process is not running. This is simple on iOS. You can use a very powerful system API called PushKit, which basically does all the work for you. In this case an open source library are saving us once again (!

That’s great! But what about Android? Well.. Here comes the trouble! 😢

Android doesn’t have a system API to handle VoIP call notification, so how do we implement this? We, unfortunately, need to write some Java from scratch. Why did I say unfortunately? Because our app is written in Javascript: all our business logic, networking, screens and other stuffs are already handled by React-native, so we don’t want to duplicate the code. We just want to popup a screen that will send an event to Native saying “I have accepted/declined the call”.

Receive push notifications when the app is not alive

The first step is writing a service which receives push notifications even if the app is not alive. For the sake of simplicity we’ll use WakefulBroadcastReceiver in this example, but we suggest using a JobScheduler to detect notifications in a better way.

The onReceive event will be triggered every time we receive a push from the GCM (remember to add the service to your AndroidManifest.xml). From here we’ll start a BackgroundService which basically just starts your React Native activity with an Intent.

Awesome! Now our React Native activity is up and running!

Now we need to show our activity over the lock screen and send the user choice (receive or decline the call) to the app.

The following steps are assuming that you are using some package to handle navigation properly inside your app. We used which is one of the most supported libs.

Based on this package, we need to extend the NavigationApplication class. This way, we get access to the lifecycle methods of our activities where we can set some flags and start our UnlockScreenActivity.

It’s really important to add the flag on the NavigationActivity, which is the activity generated by react-native-navigation that will be the container of our app. We need to put it over the lock screen, otherwise if you put it only on your “UnlockScreenActivity”, you’ll see the custom screen and after that the phone will turn the screen off again 😓

N.B. you need to set these flags before calling setContentView inside the onCreate method. Keep it in mind!

Last but not least, our UnlockScreenActivity

Great, now your Java code is ready!

We now need to connect your React Native app with a listener to the event emitted by your screen and we are ready to go:

That’s it! The magic is working! 🎩

Remarkable notes:

  • Starting from Android SDK 23, we have access to ConnectionService which is similar to what PushKit does on iOS. Probably you can get a better solution using it.
  • The activities lifecycles in this article is quite generic. Pay attention where you add the flags on the window in order to be sure to obtain the desired result.
  • We’ve followed this implementation because we didn’t want to duplicate business login and networking code between JS and Java, but maybe there is better way to handle this directly inside React Native 🙂

Are you working on a React Native app and need help? Let’s get in touch, we can support you.


  • Blog


  • react-native
  • android