PHPackages                             stitch-digital/mobile-call-detection - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. stitch-digital/mobile-call-detection

ActiveNativephp-plugin[Utility &amp; Helpers](/categories/utility)

stitch-digital/mobile-call-detection
====================================

Call detection plugin for NativePHP Mobile that fires events when phone calls end

1.0.0(3mo ago)10MITKotlin

Since Mar 10Pushed 3mo agoCompare

[ Source](https://github.com/stitch-digital/mobile-call-detection)[ Packagist](https://packagist.org/packages/stitch-digital/mobile-call-detection)[ RSS](/packages/stitch-digital-mobile-call-detection/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

Call Detection Plugin for NativePHP Mobile
==========================================

[](#call-detection-plugin-for-nativephp-mobile)

A NativePHP Mobile plugin that detects when phone calls end and fires a `CallEnded` event with call metadata.

Useful for apps that need to log calls, trigger workflows after conversations, or track call activity — such as `CRM`, `sales`, or `support` applications.

---

Platform Support
----------------

[](#platform-support)

FeatureAndroidiOSPhone number✅❌ Not available (Apple privacy)Call direction✅ `inbound` / `outbound`❌ Always `unknown`Duration✅ From call log (accurate)⚠️ From timestamps (approximate)Background detection✅ BroadcastReceiver⚠️ Limited (CXCallObserver)Permissions required✅✅ None needed### How It Works

[](#how-it-works)

**Android** uses a manifest-registered `BroadcastReceiver` that listens for `android.intent.action.PHONE_STATE`. A state machine tracks transitions between `RINGING`, `OFFHOOK`, and `IDLE` to determine when a call ends and whether it was inbound or outbound. After the call ends, the plugin waits ~1 second then queries `CallLog.Calls.CONTENT_URI` for the phone number, direction, and duration.

**iOS** uses `CXCallObserver` with a `CXCallObserverDelegate`. Apple privacy restrictions mean the plugin cannot access the phone number or call direction. It tracks `hasConnected` timestamps per call UUID to approximate duration, and dispatches the event when `hasEnded` becomes true.

---

Requirements
------------

[](#requirements)

MinimumPHP8.2NativePHP Mobile3.xAndroidAPI 21+iOS13.0+---

Installation
------------

[](#installation)

```
composer require stitch-digital/mobile-call-detection
```

The service provider and `CallDetection` facade are auto-discovered by Laravel — no manual registration needed.

---

Quick Start
-----------

[](#quick-start)

```
use StitchDigital\CallDetection\Facades\CallDetection;

// Check if permissions are granted
$granted = CallDetection::hasPermission();

// Request permissions (shows system dialog on Android, no-op on iOS)
$granted = CallDetection::requestPermission();
```

Then listen for the `CallEnded` event in your Livewire component or event listener.

---

PHP API
-------

[](#php-api)

All methods are available via the `CallDetection` facade or by resolving `StitchDigital\CallDetection\CallDetection` from the container.

### `hasPermission(): bool`

[](#haspermission-bool)

Check whether the required permissions have been granted.

- **Android** — checks `READ_PHONE_STATE` and `READ_CALL_LOG` via `ContextCompat.checkSelfPermission`.
- **iOS** — always returns `true`. `CXCallObserver` does not require explicit permission.

Returns `false` if running outside NativePHP.

```
if (CallDetection::hasPermission()) {
    // Ready to detect calls
}
```

---

### `requestPermission(): bool`

[](#requestpermission-bool)

Request call detection permissions from the user.

- **Android** — shows the system permission dialog for `READ_PHONE_STATE` and `READ_CALL_LOG`. Returns `false` immediately because the result is asynchronous — call `hasPermission()` again after the user responds.
- **iOS** — no-op, returns `true`. No permission is needed.

Returns `false` if running outside NativePHP.

```
CallDetection::requestPermission();

// After the user responds to the dialog:
$granted = CallDetection::hasPermission();
```

---

JavaScript API
--------------

[](#javascript-api)

```
import { HasPermission, RequestPermission } from '../vendor/stitch-digital/mobile-call-detection/resources/js/index.js';

const { data } = await HasPermission();
// data.granted → true/false

const { data } = await RequestPermission();
// data.granted → true/false
```

---

Events
------

[](#events)

The plugin dispatches a single event that covers the full call lifecycle completion.

EventDispatched when`CallEnded`A phone call has finished### `CallEnded` Payload

[](#callended-payload)

PropertyTypeDescription`$phoneNumber``?string`The phone number (`null` on iOS)`$direction``string``'inbound'`, `'outbound'`, or `'unknown'``$duration``int`Call duration in seconds`$platform``string``'android'` or `'ios'`### Listening with `#[OnNative]`

[](#listening-with-onnative)

```
use Native\Mobile\Attributes\OnNative;
use StitchDigital\CallDetection\Events\CallEnded;

class CallLog extends Component
{
    public array $calls = [];

    #[OnNative(CallEnded::class)]
    public function handleCallEnded(
        ?string $phoneNumber,
        string $direction,
        int $duration,
        string $platform,
    ): void {
        $this->calls[] = [
            'phoneNumber' => $phoneNumber,
            'direction' => $direction,
            'duration' => $duration,
            'platform' => $platform,
        ];
    }

    public function render()
    {
        return view('livewire.call-log');
    }
}
```

### Vue Example

[](#vue-example)

```

import { ref, onMounted, onUnmounted } from 'vue';

const calls = ref([]);

function handleCallEnded(event) {
    calls.value.push(event.detail);
}

onMounted(() => {
    window.addEventListener('native:StitchDigital\\CallDetection\\Events\\CallEnded', handleCallEnded);
});

onUnmounted(() => {
    window.removeEventListener('native:StitchDigital\\CallDetection\\Events\\CallEnded', handleCallEnded);
});

```

### React Example

[](#react-example)

```
import { useEffect, useState } from 'react';

function CallLog() {
    const [calls, setCalls] = useState([]);

    useEffect(() => {
        const handler = (event) => {
            setCalls(prev => [...prev, event.detail]);
        };

        window.addEventListener('native:StitchDigital\\CallDetection\\Events\\CallEnded', handler);
        return () => window.removeEventListener('native:StitchDigital\\CallDetection\\Events\\CallEnded', handler);
    }, []);

    return (

            {calls.map((call, i) => (
                {call.direction} — {call.duration}s
            ))}

    );
}
```

---

Platform Notes
--------------

[](#platform-notes)

### Android

[](#android)

- The `CallEndedReceiver` is registered in the AndroidManifest via the plugin manifest, so it receives call state changes **even when the app is in the background**.
- After detecting `IDLE` state, the plugin waits ~1 second before querying the call log. This delay allows the system to finish writing the log entry.
- If `READ_CALL_LOG` permission is denied but `READ_PHONE_STATE` is granted, the receiver falls back to the internal state machine for direction and duration (less accurate, no phone number).
- The receiver tracks state transitions: `RINGING → OFFHOOK → IDLE` (inbound) and `OFFHOOK → IDLE` (outbound).
- The `CallEnded` event is dispatched on the **main thread**, which is required for WebView JavaScript injection.

### iOS

[](#ios)

- `CXCallObserver` does not require explicit permission — the permission API is a no-op on iOS.
- **Phone numbers cannot be accessed** at the application level. Apple does not expose this data through `CXCallObserver`. The `phoneNumber` field is always `null`.
- **Call direction cannot be determined.** The `direction` field is always `"unknown"`.
- Duration is **approximated** from the time difference between `hasConnected` and `hasEnded` timestamps. Calls that end without connecting (missed/rejected) report a duration of `0`.
- The `CallObserverManager` singleton is initialized at app launch via `init_function` and persists for the app's lifetime.

---

Testing
-------

[](#testing)

```
# Install in your NativePHP app
composer require stitch-digital/mobile-call-detection

# Run on Android
php artisan native:run android

# Run on iOS
php artisan native:run ios
```

Then trigger the permission flow and make a test call:

**Android** — call `CallDetection::requestPermission()`, grant both permissions, then make or receive a phone call. The `CallEnded` event should fire with the phone number, direction, and duration.

**iOS** — make or receive a phone call. The `CallEnded` event should fire with `null` phoneNumber, `"unknown"` direction, and an approximate duration.

---

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

31

—

LowBetter than 66% of packages

Maintenance80

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

105d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1afc3eb4b0906148c94893fc2c76a6832d3c5f6499f6f9ab223ec6d945ee95da?d=identicon)[johntrickett](/maintainers/johntrickett)

---

Tags

nativephpnativephp-mobilenativephp-plugin

### Embed Badge

![Health badge](/badges/stitch-digital-mobile-call-detection/health.svg)

```
[![Health](https://phpackages.com/badges/stitch-digital-mobile-call-detection/health.svg)](https://phpackages.com/packages/stitch-digital-mobile-call-detection)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
