Alaznah

What @alaznah/calling autolinks, what you must add in the host app, and the JS bridges for CallKit, push, PiP, and permissions.

Native Modules

@alaznah/calling is not JavaScript-only. Voice, video, incoming UI, PiP, and kill-state wake all go through two native modules plus WebRTC.

This page is the host-app checklist: what autolinks, what you add, and what you must not duplicate.

Autolink vs host setup

Expo Go cannot load these modules. Use a React Native CLI app or an Expo development build. After any native change, rebuild — Metro reload is not enough.

After npm install @alaznah/calling + peers, autolinking registers:

Native namePlatformTypePurpose
AlaznahCallingiOS + AndroidTurbo ModulePermissions, incoming UI, CallKit/PushKit, torch, reject-from-notification, CallKit connected state
AlaznahCallingPipiOS + AndroidNative moduleSystem Picture-in-Picture enter / support / mode events
AlaznahTextureVideoViewAndroidView managerPiP TextureView (used internally)

You import JS helpers from @alaznah/calling. You do not call NativeModules.AlaznahCalling yourself unless you are debugging.

iOS — bundled automatically

The AlaznahCalling pod links CallKit, PushKit, UserNotifications, and AVKit. On load it bootstraps:

SDK nativeWhat it does
PushKit registryVoIP token for killed-state incoming
CallKit providerSystem incoming Accept / Decline
CallKit ↔ WebRTC audioMedia after a CallKit answer
Background camera helperCapture during PiP (iOS 16+)
AlaznahCallingPipPiP lifecycle → JS

Do not add your own CXProvider, PKPushRegistry, or AVPictureInPictureController in AppDelegate.

Pod target: iOS 15.1+. System video PiP needs a physical iPhone (iOS 15+).

Android — merged automatically

The library AndroidManifest merges into your app:

Merged componentPurpose
IncomingCallActivity + IncomingCallServiceFull-screen incoming when locked / killed
IncomingCallActionReceiverNotification Accept / Decline
ActiveCallKeepAliveServiceOngoing-call foreground service (mic / camera / playback types)
AlaznahPipActivityMinimize-button system PiP
supportsPictureInPicture on ${applicationId}.MainActivityHome-button Activity PiP
CallMessagingReceiverFCM incoming_call / call_canceled before React Native Firebase JS
Removes default RN Firebase messaging receiverAvoids duplicate call notifications

Host Activity code is not required for a standard RN app whose launcher is MainActivity. AlaznahCallingHostHooks attach via ActivityEventListener (onNewIntent, onUserLeaveHint, lock-screen flags, PiP listener).

What you must add (host app)

Autolinking does not replace plist strings, Xcode capabilities, the WebRTC PiP patch, or Firebase files.

1. Peer native packages

Install in your app (not inside the SDK):

npm install react-native-webrtc react-native-incall-manager @react-native-community/netinfo react-native-svg
PeerNative role
react-native-webrtcPeer connections + RTCView (iOS PiP source)
react-native-incall-managerAudio session / speaker
@react-native-community/netinfoReconnect when the radio drops
react-native-svgBuilt-in call icons

Optional: react-native-callkeep only if you explicitly turn on enableCallKeep. The SDK’s own CallKit path does not need it — a second provider on iOS is harmful.

2. iOS — Info.plist + capabilities

xml
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for audio and video calls.</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is required for video calls.</string>
<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
  <string>voip</string>
  <string>remote-notification</string>
</array>

Xcode → Signing & Capabilities:

CapabilityNeeded for
Background Modes → AudioCall audio + video PiP in background
Background Modes → Voice over IPPushKit wake when killed
Push NotificationsAPNs / VoIP delivery

Then:

bash
cd ios && pod install && cd ..

3. iOS — WebRTC PiP patch

Required for production iOS Picture-in-Picture. Patch files live in the SDK; point patch-package at them:

bash
npm i -D patch-package
json
{
  "scripts": {
    "postinstall": "patch-package --patch-dir node_modules/@alaznah/calling/patches"
  }
}

Reinstall and rebuild iOS. Details: Picture in Picture.

4. Android — permissions

The library already declares camera, mic, FGS types, full-screen intent, and related permissions. If merge is incomplete, copy them into your android/app/src/main/AndroidManifest.xml:

xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

ActiveCallKeepAliveService uses microphone / camera / mediaPlayback (and phone-call as a fallback). Android 14+ will drop a mis-typed FGS.

Runtime: requestCallPermissions('audio' | 'video') before the first call. On API 33+ also request POST_NOTIFICATIONS. On API 34+, the user may need to allow full-screen intents in system settings for lock-screen incoming.

Release minify: the AAR ships consumer-rules.pro that keeps WebRTC / PiP reflection (com.oney.WebRTCModule.*, org.webrtc.**). You should not copy those rules unless merge fails. Enable minifyEnabled as you normally would.

5. Android — custom Activity name

Only if the launcher is not ${applicationId}.MainActivityPicture in Picture.

Do not add CallMessagingReceiver, IncomingCallActivity, or onUserLeaveHint PiP code yourself.

6. Android — Firebase (kill-state incoming)

For ringing when the app is killed:

  1. google-services.json in android/app/
  2. @react-native-firebase/app + @react-native-firebase/messaging
  3. FCM server key in the Developer Console
  4. registerPushToken(token, 'android')

The SDK native receiver handles call payloads first. Other FCM messages still reach React Native Firebase when that package is installed.

Full push walkthrough: Background Calling.

7. Expo development build

json
{
  "expo": {
    "plugins": ["@alaznah/calling"]
  }
}

The plugin injects camera/mic usage strings, UIBackgroundModes (audio, voip, remote-notification), and the Android permission list. Then npx expo prebuild (or EAS) and verify the generated plist / manifest.

JavaScript bridges (use these, not raw native)

ts
import {
  requestCallPermissions,
  registerIosVoipToken,
  onIosVoipToken,
  handleBackgroundIncomingCall,
  consumeNativeIncomingAction,
} from '@alaznah/calling';

// Before startCall / accept
await requestCallPermissions('video');

// iOS kill-state ring — VoIP token, not FCM
const voip = await registerIosVoipToken();
const stop = onIosVoipToken((token) => {
  void client.registerPushToken(token, 'ios');
});

// After native Accept / Decline, before showing JS incoming UI
await client.drainNativeIncomingAction();
await client.syncPendingCalls();
HelperWhen
requestCallPermissionsFirst call; maps to the native permission module
registerIosVoipToken / onIosVoipTokeniOS PushKit token → registerPushToken(..., 'ios')
client.drainNativeIncomingActionResume after CallKit / Android full-screen Accept
client.setNativeIncomingSuppressedHide native incoming while in-app UI is visible
handleBackgroundIncomingCallOptional FCM data while the app is already open
useWakingForCall()Cold-start accept in progress

CallingProvider already drains native actions and reconnects on AppStateactive. Custom navigation still needs the same resume hooks — Incoming Calls.

The SDK also calls native methods internally (you do not wire these):

Native methodUsed for
configureCallEndpointAndroid notification Decline can POST /call/reject without opening JS
storeRejectTokenShort-lived grant from the invite push
showIncoming / cancelNative incoming UI
enableBackgroundCameraiOS PiP camera
reportCallConnectedCallKit PiP mute / end
hasCameraTorch / setCameraTorchFlash when torchAvailable

Rebuild

bash
cd ios && pod install && cd ..
npx react-native run-ios
npx react-native run-android

Do not add in the host app

Tempting copy-pasteWhy not
Second CallKit provider / CallKeep on iOSConflicts with AlaznahCallingManager
Custom AVPictureInPictureControllerSDK + patched WebRTC own the remote view
MainActivity.onUserLeaveHintenterPictureInPictureModeAlready hooked
Extra FCM receiver for Alaznah call payloadsCallMessagingReceiver already merged
Expo GoNative modules are not in that binary

Checklist

  • Peers installed and native rebuild done
  • iOS usage strings + Background Modes + Push capability
  • postinstall webrtc patch (iOS PiP)
  • Android permissions merged (including FGS microphone / camera)
  • Launcher Activity is MainActivity, or PiP flags added manually
  • (Incoming when killed) FCM / APNs credentials + token registration
  • CallingProvider + CallingUI / CallingScreen mounted
  • config.displayName and startCall({ calleeDisplayName }) set (required in current SDK)