Skip to content

Commit 2e17528

Browse files
authored
docs: add incoming call kit plugin docs (#567)
1 parent 004ebf9 commit 2e17528

10 files changed

Lines changed: 549 additions & 109 deletions

File tree

apps/docs/src/config/llmsCustomSets.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Plugin Home Indicator|iOS home indicator visibility control plugin|docs/plugins/
4343
Plugin iBeacon|iBeacon proximity detection plugin|docs/plugins/ibeacon/**
4444
Plugin InAppBrowser|in-app browser plugin for opening web content|docs/plugins/inappbrowser/**
4545
Plugin In App Review|in-app review prompt plugin for app store ratings|docs/plugins/in-app-review/**
46+
Plugin Incoming Call Kit|native incoming call presentation with iOS CallKit and Android full-screen notifications|docs/plugins/incoming-call-kit/**
4647
Plugin Intent Launcher|Android intent launcher plugin|docs/plugins/intent-launcher/**
4748
Plugin Intercom|Intercom customer messaging and support plugin for native in-app chat|docs/plugins/intercom/**
4849
Plugin Is Root|root/jailbreak detection plugin|docs/plugins/is-root/**

apps/docs/src/config/sidebar.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const pluginEntries = [
9090
['iBeacon', 'ibeacon'],
9191
['In App Review', 'in-app-review'],
9292
['InAppBrowser', 'inappbrowser'],
93+
['Incoming Call Kit', 'incoming-call-kit', [linkItem('iOS', '/docs/plugins/incoming-call-kit/ios'), linkItem('Android', '/docs/plugins/incoming-call-kit/android')]],
9394
['Intent Launcher', 'intent-launcher'],
9495
['Intercom', 'intercom'],
9596
['Is Root', 'is-root'],
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: Android
3+
description: Configure notifications, full-screen intents, and Android-specific incoming-call behavior.
4+
sidebar:
5+
order: 4
6+
---
7+
8+
## How Android behavior works
9+
10+
On Android, the plugin posts a high-priority incoming-call notification and can raise a full-screen activity when the platform and user settings allow it.
11+
12+
The plugin manifest already includes:
13+
14+
```xml
15+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
16+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
17+
```
18+
19+
After installation, `cap sync` is enough to merge that configuration into your host app.
20+
21+
## Runtime permissions
22+
23+
Call these methods during onboarding or before you rely on incoming-call presentation:
24+
25+
```ts
26+
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';
27+
28+
await IncomingCallKit.requestPermissions();
29+
await IncomingCallKit.requestFullScreenIntentPermission();
30+
```
31+
32+
- `requestPermissions()` requests notification permission on Android 13 and later.
33+
- `requestFullScreenIntentPermission()` opens the Android 14 and later settings page for full-screen intents when needed.
34+
35+
## Basic example
36+
37+
```ts
38+
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';
39+
40+
await IncomingCallKit.showIncomingCall({
41+
callId: 'call-42',
42+
callerName: 'Ada Lovelace',
43+
appName: 'Capgo Phone',
44+
timeoutMs: 45_000,
45+
android: {
46+
channelId: 'calls',
47+
channelName: 'Incoming Calls',
48+
showFullScreen: true,
49+
isHighPriority: true,
50+
accentColor: '#0F766E',
51+
},
52+
});
53+
```
54+
55+
## Android-specific options
56+
57+
- `channelId`: identifier for the notification channel
58+
- `channelName`: user-visible channel name
59+
- `showFullScreen`: request the full-screen activity
60+
- `isHighPriority`: keep the notification disruptive enough for ringing flows
61+
- `accentColor`: tint compatible notification surfaces
62+
- `ringtoneUri`: point at a custom Android ringtone resource or URI
63+
64+
## Behavior notes
65+
66+
- Full-screen presentation is best-effort. If the device or user settings block it, Android still shows the incoming-call notification.
67+
- Timeout handling is best-effort. The plugin tracks `timeoutMs` and emits `callTimedOut`, but your backend should still reconcile missed calls on its side.
68+
- Accept, decline, and end actions are emitted back through Capacitor listeners so your app can join or clean up the real call session.
69+
70+
## Recommended production model
71+
72+
Use Android push or your calling SDK for transport, then let this plugin handle the last mile of native ringing UI. Keep these responsibilities outside the plugin:
73+
74+
- FCM registration and token management
75+
- Media session lifecycle
76+
- Backend call state
77+
- Retry and missed-call business logic
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
title: Getting Started
3+
description: Install and wire incoming call presentation in Capacitor with a transport-agnostic API.
4+
sidebar:
5+
order: 2
6+
---
7+
8+
import { PackageManagers } from 'starlight-package-managers'
9+
import { Steps } from '@astrojs/starlight/components';
10+
11+
<Steps>
12+
1. **Install the package**
13+
<PackageManagers pkg="@capgo/capacitor-incoming-call-kit" pkgManagers={['npm', 'pnpm', 'yarn', 'bun']} />
14+
15+
2. **Sync native projects**
16+
<PackageManagers type="exec" pkg="cap" args="sync" pkgManagers={['npm', 'pnpm', 'yarn', 'bun']} />
17+
18+
3. **Choose your ring source**
19+
Decide whether the incoming-call event comes from your backend, an SDK such as Twilio or Stream, or a native push path such as FCM or PushKit.
20+
</Steps>
21+
22+
## How the integration fits together
23+
24+
This plugin only owns native incoming-call presentation. Your app still owns transport, authentication, and the actual media session.
25+
26+
The common production pattern is:
27+
28+
1. Your backend or calling SDK emits a ring event.
29+
2. Your app calls `showIncomingCall()`.
30+
3. The plugin presents native incoming-call UI.
31+
4. `callAccepted` tells your app to join the actual room or VoIP session.
32+
5. `callDeclined`, `callEnded`, or `callTimedOut` tells your app to clean up remote state.
33+
34+
## Minimal integration
35+
36+
```ts
37+
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';
38+
39+
await IncomingCallKit.requestPermissions();
40+
await IncomingCallKit.requestFullScreenIntentPermission();
41+
42+
await IncomingCallKit.addListener('callAccepted', async ({ call }) => {
43+
console.log('Accepted', call.callId, call.extra);
44+
// Start or join your real call session here.
45+
});
46+
47+
await IncomingCallKit.addListener('callDeclined', ({ call }) => {
48+
console.log('Declined', call.callId);
49+
// Tell your backend or SDK that the user declined.
50+
});
51+
52+
await IncomingCallKit.addListener('callTimedOut', ({ call }) => {
53+
console.log('Timed out', call.callId);
54+
// Clear ringing state in your backend or SDK.
55+
});
56+
57+
await IncomingCallKit.showIncomingCall({
58+
callId: 'call-42',
59+
callerName: 'Ada Lovelace',
60+
handle: '+39 555 010 020',
61+
appName: 'Capgo Phone',
62+
hasVideo: true,
63+
timeoutMs: 45_000,
64+
extra: {
65+
roomId: 'room-42',
66+
callerUserId: 'user_ada',
67+
},
68+
android: {
69+
channelId: 'calls',
70+
channelName: 'Incoming Calls',
71+
showFullScreen: true,
72+
},
73+
ios: {
74+
handleType: 'phoneNumber',
75+
},
76+
});
77+
```
78+
79+
## Important options
80+
81+
- `callId`: stable identifier reused later with `endCall()`
82+
- `timeoutMs`: best-effort unanswered timeout
83+
- `extra`: arbitrary JSON echoed back in listener payloads
84+
- `android.channelId` and `android.channelName`: Android notification channel tuning
85+
- `android.showFullScreen`: requests the Android full-screen incoming-call activity
86+
- `ios.handleType`: choose `generic`, `phoneNumber`, or `emailAddress` for CallKit
87+
88+
## Managing active calls
89+
90+
```ts
91+
const { calls } = await IncomingCallKit.getActiveCalls();
92+
93+
await IncomingCallKit.endCall({
94+
callId: 'call-42',
95+
reason: 'remote-ended',
96+
});
97+
98+
await IncomingCallKit.endAllCalls({
99+
reason: 'session-reset',
100+
});
101+
```
102+
103+
## Event model
104+
105+
- `incomingCallDisplayed`: native UI was shown successfully
106+
- `callAccepted`: user accepted from the native UI
107+
- `callDeclined`: user declined before joining
108+
- `callEnded`: your app or the platform ended the tracked call
109+
- `callTimedOut`: the call stayed unanswered until `timeoutMs`
110+
111+
Each event carries the normalized `call` payload and your original `extra` object.
112+
113+
## Platform notes
114+
115+
- Read the [iOS guide](/docs/plugins/incoming-call-kit/ios/) before wiring CallKit into a PushKit or APNs flow.
116+
- Read the [Android guide](/docs/plugins/incoming-call-kit/android/) before relying on full-screen intents on Android 14 and later.
117+
- Web is not supported.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: "@capgo/capacitor-incoming-call-kit"
3+
description: Native incoming call presentation for Capacitor with iOS CallKit and Android full-screen notifications.
4+
tableOfContents: false
5+
next: false
6+
prev: false
7+
sidebar:
8+
order: 1
9+
label: "Introduction"
10+
hero:
11+
tagline: Present native incoming-call UI in Capacitor with iOS CallKit and Android full-screen notifications while keeping your push and media stack under your control.
12+
image:
13+
file: ~public/icons/plugins/incoming-call-kit.svg
14+
actions:
15+
- text: Get started
16+
link: /docs/plugins/incoming-call-kit/getting-started/
17+
icon: right-arrow
18+
variant: primary
19+
- text: GitHub
20+
link: https://github.com/Cap-go/capacitor-incoming-call-kit/
21+
icon: external
22+
variant: minimal
23+
---
24+
25+
import { Card, CardGrid } from '@astrojs/starlight/components';
26+
27+
## Overview
28+
29+
`@capgo/capacitor-incoming-call-kit` gives your Capacitor app the native ringing surface for incoming calls without forcing a specific VoIP vendor, push transport, or backend architecture.
30+
31+
Use it when you already have your own signaling flow, SIP stack, Twilio integration, Stream integration, FCM delivery, or PushKit delivery and want the platform-native incoming-call experience layered onto that flow.
32+
33+
<CardGrid stagger>
34+
<Card title="CallKit on iOS" icon="approve-check">
35+
Report incoming calls to CallKit and react to accepted, declined, ended, and timed-out events.
36+
</Card>
37+
<Card title="Full-screen Android UI" icon="rocket">
38+
Show a high-priority notification and raise a full-screen activity when Android allows it.
39+
</Card>
40+
<Card title="Buffered listener events" icon="star">
41+
User actions are retained until the Capacitor bridge consumes them, which helps when JavaScript starts late.
42+
</Card>
43+
<Card title="Transport agnostic" icon="setting">
44+
Bring your own FCM, APNs, PushKit, SIP, or backend event flow. The plugin focuses on presentation, not transport.
45+
</Card>
46+
<Card title="Typed cross-platform API" icon="comment">
47+
Use one small API to show calls, end calls, inspect active calls, and listen for state changes.
48+
</Card>
49+
<Card title="Platform guides" icon="open-book">
50+
Start with the [Getting started guide](/docs/plugins/incoming-call-kit/getting-started/), then read the [iOS guide](/docs/plugins/incoming-call-kit/ios/) and [Android guide](/docs/plugins/incoming-call-kit/android/).
51+
</Card>
52+
</CardGrid>
53+
54+
## What the plugin handles
55+
56+
- Native incoming-call presentation
57+
- Active call tracking
58+
- Accept, decline, end, and timeout events
59+
- Android notification and full-screen intent wiring
60+
- iOS CallKit reporting
61+
62+
## What the plugin does not handle
63+
64+
- FCM, APNs, or PushKit registration
65+
- Starting or managing the real audio or video session
66+
- Vendor-specific call logic from Twilio, Stream, Daily, Agora, SIP, or WebRTC SDKs
67+
68+
If you need a complete communications stack, use this plugin as the native incoming-call surface and keep transport plus media sessions in your existing backend or SDK.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
title: iOS
3+
description: Configure CallKit behavior and understand production limits on iOS.
4+
sidebar:
5+
order: 3
6+
---
7+
8+
## How iOS behavior works
9+
10+
On iOS, the plugin reports the incoming call to CallKit. That gives you the system incoming-call sheet and standardized call actions without building your own native incoming-call UI.
11+
12+
`requestPermissions()` resolves immediately on iOS because CallKit itself does not require a runtime permission dialog.
13+
14+
## Basic example
15+
16+
```ts
17+
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';
18+
19+
await IncomingCallKit.showIncomingCall({
20+
callId: 'call-42',
21+
callerName: 'Ada Lovelace',
22+
handle: '+1 555 010 020',
23+
ios: {
24+
handleType: 'phoneNumber',
25+
supportsHolding: true,
26+
supportsDTMF: false,
27+
},
28+
});
29+
```
30+
31+
## Handle types
32+
33+
Use `ios.handleType` to control how CallKit formats the handle:
34+
35+
- `generic` for app-specific identifiers
36+
- `phoneNumber` for real phone numbers
37+
- `emailAddress` for email-based identities
38+
39+
## Background incoming calls
40+
41+
This plugin does not register PushKit or APNs for you.
42+
43+
For true background or terminated-state ringing on iOS, your host app still needs the native Apple push setup that matches your transport strategy:
44+
45+
1. Enable Push Notifications when your transport uses Apple push delivery.
46+
2. Enable the Voice over IP background mode when your app uses a VoIP push flow.
47+
3. Deliver the incoming-call event to your app and invoke this plugin as soon as the Capacitor bridge is available.
48+
49+
If your ring event exists only in JavaScript, you will get the best experience while the app is already running in the foreground.
50+
51+
## Microphone and camera permissions
52+
53+
CallKit does not replace your media SDK. If the real call session uses microphone or camera access, those usage descriptions still belong in your app:
54+
55+
```xml
56+
<key>NSMicrophoneUsageDescription</key>
57+
<string>This app uses the microphone for calls.</string>
58+
<key>NSCameraUsageDescription</key>
59+
<string>This app uses the camera for video calls.</string>
60+
```
61+
62+
Add only the keys your real calling flow needs.
63+
64+
## Keep these responsibilities in your app layer
65+
66+
- PushKit and APNs registration
67+
- Authentication and token refresh
68+
- Joining the real room or VoIP session after `callAccepted`
69+
- Ending or reconciling remote call state when the plugin emits `callDeclined`, `callEnded`, or `callTimedOut`
Lines changed: 6 additions & 0 deletions
Loading

apps/web/src/config/plugins.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const actionDefinitionRows =
9292
@capgo/capacitor-age-range|github.com/Cap-go|Cross-platform age range detection using Google Play Age Signals (Android) and Apple DeclaredAgeRange (iOS)|https://github.com/Cap-go/capacitor-age-range/|Age Range
9393
@capgo/capacitor-persona|github.com/Cap-go|Launch Persona identity verification inquiries with native iOS and Android SDKs|https://github.com/Cap-go/capacitor-persona/|Persona
9494
@capgo/capacitor-intune|github.com/Cap-go|Microsoft Intune MAM, app protection policy, app config, and MSAL authentication for Capacitor|https://github.com/Cap-go/capacitor-intune/|Intune
95+
@capgo/capacitor-incoming-call-kit|github.com/Cap-go|Present native incoming-call UI with iOS CallKit and Android full-screen notifications|https://github.com/Cap-go/capacitor-incoming-call-kit/|Incoming Call Kit
9596
@capgo/capacitor-android-age-signals|github.com/Cap-go|Google Play Age Signals API wrapper - detect supervised accounts and verified users|https://github.com/Cap-go/capacitor-android-age-signals/|Age Signals
9697
@capgo/capacitor-barometer|github.com/Cap-go|Access device barometer for atmospheric pressure and altitude readings|https://github.com/Cap-go/capacitor-barometer/|Barometer
9798
@capgo/capacitor-accelerometer|github.com/Cap-go|Read device accelerometer for motion detection and orientation tracking|https://github.com/Cap-go/capacitor-accelerometer/|Accelerometer
@@ -214,6 +215,7 @@ const pluginIconsByName: Record<string, string> = {
214215
'@capgo/capacitor-age-range': 'UserGroup',
215216
'@capgo/capacitor-persona': 'UserCircle',
216217
'@capgo/capacitor-intune': 'ShieldCheck',
218+
'@capgo/capacitor-incoming-call-kit': 'Phone',
217219
'@capgo/capacitor-android-age-signals': 'UserGroup',
218220
'@capgo/capacitor-barometer': 'ChartBar',
219221
'@capgo/capacitor-accelerometer': 'ArrowsPointingOut',

0 commit comments

Comments
 (0)