Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ export default defineConfig({
description: 'in-app review prompt plugin for app store ratings',
paths: ['docs/plugins/in-app-review/**'],
},
{
label: 'Plugin Incoming Call Kit',
description: 'native incoming call UI plugin with iOS CallKit and Android full-screen notifications',
paths: ['docs/plugins/incoming-call-kit/**'],
},
{
label: 'Plugin Intent Launcher',
description: 'Android intent launcher plugin',
Expand Down Expand Up @@ -1089,6 +1094,16 @@ export default defineConfig({
],
collapsed: true,
},
{
label: 'Incoming Call Kit',
items: [
{ label: 'Overview', link: '/docs/plugins/incoming-call-kit/' },
{ label: 'Getting started', link: '/docs/plugins/incoming-call-kit/getting-started' },
{ label: 'iOS', link: '/docs/plugins/incoming-call-kit/ios' },
{ label: 'Android', link: '/docs/plugins/incoming-call-kit/android' },
],
collapsed: true,
},
{
label: 'Intercom',
items: [
Expand Down
6 changes: 6 additions & 0 deletions public/icons/plugins/incoming-call-kit.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/config/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ export const actions = [
title: 'Text Interaction',
icon: 'DocumentText',
},
{
name: '@capgo/capacitor-incoming-call-kit',
author: 'github.com/Cap-go',
description: 'Present native incoming call UI with iOS CallKit and Android full-screen notifications',
href: 'https://github.com/Cap-go/capacitor-incoming-call-kit/',
title: 'Incoming Call Kit',
icon: 'Phone',
},
{
name: '@capgo/capacitor-twilio-voice',
author: 'github.com/Cap-go',
Expand Down
75 changes: 75 additions & 0 deletions src/content/docs/docs/plugins/incoming-call-kit/android.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: Android
description: Configure notifications, full-screen intents, and Android-specific incoming call behavior.
sidebar:
order: 4
---

## How Android behavior works

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.

The plugin manifest already includes:

```xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
```

After installation, `bunx cap sync` is enough to merge that configuration into your host app.

## Runtime permissions

Call these methods during onboarding or before you rely on incoming-call presentation:

```ts
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';

await IncomingCallKit.requestPermissions();
await IncomingCallKit.requestFullScreenIntentPermission();
```

- `requestPermissions()` requests notification permission on Android 13 and later.
- `requestFullScreenIntentPermission()` opens the Android 14 and later settings page for full-screen intents when needed.

## Basic example

```ts
await IncomingCallKit.showIncomingCall({
callId: 'call-42',
callerName: 'Ada Lovelace',
appName: 'Capgo Phone',
timeoutMs: 45_000,
android: {
channelId: 'calls',
channelName: 'Incoming Calls',
showFullScreen: true,
isHighPriority: true,
accentColor: '#0F766E',
},
});
```

## Android-specific options

- `channelId`: identifier for the notification channel.
- `channelName`: user-visible channel name.
- `showFullScreen`: request the full-screen activity.
- `isHighPriority`: keep the notification disruptive enough for ringing flows.
- `accentColor`: tint compatible notification surfaces.
- `ringtoneUri`: point at a custom Android ringtone resource or URI.

## Behavior notes

- Full-screen presentation is best-effort. If the device or user settings block it, Android still shows the incoming-call notification.
- Timeout handling is also best-effort. The plugin tracks `timeoutMs` and emits `callTimedOut`, but your backend should still reconcile missed calls on its side.
- Accept, decline, and end actions are emitted back through Capacitor listeners so your app can join or clean up the real call session.

## Recommended production model

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:

- FCM registration and token management
- Media session lifecycle
- Backend call state
- Retry and missed-call business logic
109 changes: 109 additions & 0 deletions src/content/docs/docs/plugins/incoming-call-kit/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
title: Getting Started
description: Install and wire incoming call presentation in Capacitor with a small, transport-agnostic API.
sidebar:
order: 2
---

## Install

```bash
bun add @capgo/capacitor-incoming-call-kit
bunx cap sync
```

## Decide where the ring event comes from

Before writing code, decide how your app learns that someone is calling:

1. A foreground JavaScript event from your backend.
2. An Android FCM notification or iOS PushKit/APNs flow.
3. A vendor SDK such as Twilio, Stream, SIP, or your own native layer.

When that ring event reaches your app, call `showIncomingCall()`. When the user accepts, join your real voice or video session from your own SDK or backend flow.

## Minimal integration

```ts
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';

await IncomingCallKit.requestPermissions();
await IncomingCallKit.requestFullScreenIntentPermission();

await IncomingCallKit.addListener('callAccepted', async ({ call }) => {
console.log('Accepted', call.callId, call.extra);
// Start or join your real call session here.
});

await IncomingCallKit.addListener('callDeclined', ({ call }) => {
console.log('Declined', call.callId);
// Tell your backend or SDK that the user declined.
});

await IncomingCallKit.addListener('callTimedOut', ({ call }) => {
console.log('Timed out', call.callId);
// Clear ringing state in your backend or SDK.
});

await IncomingCallKit.showIncomingCall({
callId: 'call-42',
callerName: 'Ada Lovelace',
handle: '+39 555 010 020',
appName: 'Capgo Phone',
hasVideo: true,
timeoutMs: 45_000,
extra: {
roomId: 'room-42',
callerUserId: 'user_ada',
},
android: {
channelId: 'calls',
channelName: 'Incoming Calls',
showFullScreen: true,
},
ios: {
handleType: 'phoneNumber',
},
});
```

## Important options

- `callId`: stable identifier you reuse later with `endCall()`.
- `timeoutMs`: best-effort unanswered timeout.
- `extra`: arbitrary JSON echoed back in all listener payloads.
- `android.channelId` and `android.channelName`: tune the Android notification channel.
- `android.showFullScreen`: request the Android full-screen incoming-call activity.
- `ios.handleType`: choose `generic`, `phoneNumber`, or `emailAddress` for CallKit.

## Managing active calls

```ts
const { calls } = await IncomingCallKit.getActiveCalls();

await IncomingCallKit.endCall({
callId: 'call-42',
reason: 'remote-ended',
});

await IncomingCallKit.endAllCalls({
reason: 'session-reset',
});
```

## Production architecture

The safest model is:

1. Your backend or calling SDK emits a ring event.
2. Your app calls `showIncomingCall()`.
3. The plugin shows native UI.
4. `callAccepted` tells your app to join the actual room or VoIP session.
5. `callDeclined`, `callEnded`, or `callTimedOut` tells your app to clean up remote state.

Keep the plugin focused on native presentation. Keep call transport, authentication, and media session ownership in the system you already trust for those responsibilities.

## Platform setup

- Read the [iOS guide](/docs/plugins/incoming-call-kit/ios/) before wiring CallKit into a PushKit or APNs flow.
- Read the [Android guide](/docs/plugins/incoming-call-kit/android/) before relying on full-screen intents on Android 14 and later.
68 changes: 68 additions & 0 deletions src/content/docs/docs/plugins/incoming-call-kit/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: "@capgo/capacitor-incoming-call-kit"
description: Native incoming call presentation for Capacitor with iOS CallKit and Android full-screen notifications.
tableOfContents: false
next: false
prev: false
sidebar:
order: 1
label: "Introduction"
hero:
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.
image:
file: ~public/icons/plugins/incoming-call-kit.svg
actions:
- text: Get started
link: /docs/plugins/incoming-call-kit/getting-started/
icon: right-arrow
variant: primary
- text: GitHub
link: https://github.com/Cap-go/capacitor-incoming-call-kit/
icon: external
variant: minimal
---

import { Card, CardGrid } from '@astrojs/starlight/components';

## Overview

`@capgo/capacitor-incoming-call-kit` gives your Capacitor app the native ringing layer for incoming calls without forcing a specific VoIP vendor or push provider.

Use it when you already have your own signaling flow, SIP stack, Twilio integration, Stream integration, FCM delivery, or PushKit delivery and you want the platform-native incoming call experience on top of that.

<CardGrid stagger>
<Card title="CallKit on iOS" icon="approve-check">
Report incoming calls to CallKit and react to accepted, declined, ended, and timed-out events.
</Card>
<Card title="Full-screen Android UI" icon="rocket">
Show a high-priority notification and raise a full-screen activity when Android allows it.
</Card>
<Card title="Buffered listener events" icon="star">
User actions are retained until the Capacitor bridge consumes them, which helps when JavaScript starts late.
</Card>
<Card title="Transport agnostic" icon="setting">
Bring your own FCM, APNs, PushKit, SIP, or backend event flow. The plugin focuses on presentation, not transport.
</Card>
<Card title="Typed cross-platform API" icon="comment">
Use one small API to show calls, end calls, inspect active calls, and listen for state changes.
</Card>
<Card title="Platform guides" icon="open-book">
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/).
</Card>
</CardGrid>

## What the plugin handles

- Native incoming call presentation
- Active call tracking
- Accept, decline, end, and timeout events
- Android notification and full-screen intent wiring
- iOS CallKit reporting

## What the plugin does not handle

- FCM, APNs, or PushKit registration
- Starting or managing the real audio or video session
- Vendor-specific call logic from Twilio, Stream, Daily, Agora, SIP, or WebRTC SDKs

If you need a complete communications stack, use this plugin as the native incoming-call surface and keep your transport and media session in your existing backend or SDK.
69 changes: 69 additions & 0 deletions src/content/docs/docs/plugins/incoming-call-kit/ios.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: iOS
description: Configure CallKit behavior and understand production limits on iOS.
sidebar:
order: 3
---

## How iOS behavior works

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

`requestPermissions()` resolves immediately on iOS because CallKit itself does not require a runtime permission dialog.

## Basic example

```ts
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';

await IncomingCallKit.showIncomingCall({
callId: 'call-42',
callerName: 'Ada Lovelace',
handle: '+1 555 010 020',
ios: {
handleType: 'phoneNumber',
supportsHolding: true,
supportsDTMF: false,
},
});
```

## Handle types

Use the `ios.handleType` option to control how CallKit formats the handle:

- `generic` for app-specific identifiers
- `phoneNumber` for real phone numbers
- `emailAddress` for email-based identities

## If you need real background incoming calls

This plugin does not register PushKit or APNs for you.

For true background or terminated-state ringing on iOS, your host app still needs the native Apple push setup that matches your transport strategy:

1. Enable the Push Notifications capability when your transport uses Apple push delivery.
2. Enable the Voice over IP background mode when your app uses a VoIP push flow.
3. Deliver the incoming-call event to your app and invoke this plugin as soon as the Capacitor bridge is available.

If your ring event exists only in JavaScript, you will get the best experience while the app is already running in the foreground.

## Microphone and camera permissions

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:

```xml
<key>NSMicrophoneUsageDescription</key>
<string>This app uses the microphone for calls.</string>
<key>NSCameraUsageDescription</key>
<string>This app uses the camera for video calls.</string>
```

Add only the keys your real calling flow needs.

## What to keep in your app layer

- PushKit and APNs registration
- Authentication and token refresh
- Joining the real room or VoIP session after `callAccepted`
- Ending or reconciling remote call state when the plugin emits `callDeclined`, `callEnded`, or `callTimedOut`
5 changes: 5 additions & 0 deletions src/content/docs/docs/plugins/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ The Updater plugin is the foundation of Capgo Cloud, enabling you to:
## 📞 Communication & Analytics

<CardGrid>
<LinkCard
title="Incoming Call Kit"
description="Present CallKit on iOS and full-screen incoming call UI on Android without locking into a specific VoIP provider."
href="/docs/plugins/incoming-call-kit/"
/>
<LinkCard
title="Twilio Voice"
description="Make and receive phone calls using Twilio Voice API."
Expand Down
Loading