codingwithrk / no-screenshot
A NativePHP Mobile plugin that prevents screenshots, blocks screen recording and global protection in App-switch state in your mobile app.
Package info
github.com/codingwithrk/no-screenshot
Type:nativephp-plugin
pkg:composer/codingwithrk/no-screenshot
Requires
- php: ^8.2
- nativephp/mobile: ^3.0
Requires (Dev)
- pestphp/pest: ^3.0
README
A NativePHP Mobile plugin that prevents screenshots, blocks screen recording and global protection in App-switch state in your mobile app.
Mainly useful for apps that handle sensitive data, such as financial, healthcare, or enterprise applications, where protecting user privacy and data security is paramount.
Platform Support
| Feature | Android | iOS |
|---|---|---|
| Block screenshots | ✅ | ⚠️ Detected, not preventable |
| Block screen recording | ✅ | ✅ Black overlay |
| Detect live recording | — | ✅ |
| Global protection | ✅ | ✅ |
How It Works
Android uses WindowManager.LayoutParams.FLAG_SECURE — an OS-level window flag that prevents the system from capturing the screen by any means: the screenshot button, the built-in screen recorder, ADB shell, and third-party capture apps all receive a blank or blocked frame.
iOS cannot prevent the system screenshot gesture at the application level. Instead the plugin:
- Observes
UIScreen.capturedDidChangeNotification - When recording starts and protection is active, immediately overlays a full-screen black
UIWindow(above the status bar) that hides all WebView content - Reports
isScreenBeingRecordedin real time viaUIScreen.main.isCaptured
Requirements
| Minimum | |
|---|---|
| PHP | 8.2 |
| NativePHP Mobile | 3.x |
| Android | API 21 (Android 5.0 Lollipop) |
| iOS | 13.0 |
FLAG_SECUREis available from Android API 1. API 21 is the minimum set to match NativePHP Mobile's own requirements. The iOS 13 minimum is required because the recording overlay usesUIWindowScene, introduced in iOS 13.
Installation
composer require codingwithrk/no-screenshot
The service provider and NoScreenshot facade alias are auto-discovered by Laravel — no manual registration needed.
Quick Start
use Codingwithrk\NoScreenshot\Facades\NoScreenshot; // Protect the entire app NoScreenshot::disableGlobally(); // Lift global protection NoScreenshot::enableGlobally();
PHP API
All methods are available via the NoScreenshot facade or by resolving Codingwithrk\NoScreenshot\NoScreenshot from the container.
disableGlobally(): bool
Activates protection for the entire app.
- Android — adds
FLAG_SECUREto the activity window; all capture attempts receive a blank frame. - iOS — registers the
UIScreen.capturedDidChangeNotificationobserver; if recording is already in progress, the black overlay appears immediately.
Returns true on success, false if running outside NativePHP.
NoScreenshot::disableGlobally();
enableGlobally(): bool
Removes global protection.
NoScreenshot::enableGlobally();
toggle(): bool
Toggles global protection on/off. Returns the new isGloballyProtected state.
$isNowProtected = NoScreenshot::toggle();
getStatus(): ?ScreenProtectionStatus
Returns the current protection state as a typed DTO, or null outside NativePHP.
$status = NoScreenshot::getStatus(); $status->isGloballyProtected; // bool — true after disableGlobally() $status->isScreenBeingRecorded; // bool — iOS: live UIScreen.main.isCaptured; Android: always false $status->isScreenshotDetectionActive; // bool — true when screenshot detection is running
Events
Three events cover the full lifecycle of capture activity. Subscribe to them in your Livewire components or event listeners.
| Event | Dispatched when |
|---|---|
ScreenshotAttempted |
A screenshot was taken (detected, cannot be blocked on iOS) |
ScreenRecordingStarted |
isScreenBeingRecorded transitions false → true |
ScreenRecordingStopped |
isScreenBeingRecorded transitions true → false |
Android note:
FLAG_SECUREprevents capture rather than detecting it, so events are not dispatched automatically. Dispatch them manually from your polling logic if needed.
Listening with #[OnNative]
use Native\Mobile\Attributes\OnNative; use Codingwithrk\NoScreenshot\Events\ScreenshotAttempted; use Codingwithrk\NoScreenshot\Events\ScreenRecordingStarted; use Codingwithrk\NoScreenshot\Events\ScreenRecordingStopped; class MyLivewireComponent extends Component { #[OnNative(ScreenshotAttempted::class)] public function onScreenshotAttempted(): void { // Log the attempt, notify the user, etc. logger()->warning('Screenshot attempted'); } #[OnNative(ScreenRecordingStarted::class)] public function onRecordingStarted(): void { $this->dispatch('recording-started'); // trigger frontend update } #[OnNative(ScreenRecordingStopped::class)] public function onRecordingStopped(): void { $this->dispatch('recording-stopped'); } }
Manual Dispatch from a Controller
use Codingwithrk\NoScreenshot\Facades\NoScreenshot; use Codingwithrk\NoScreenshot\Events\ScreenRecordingStarted; use Codingwithrk\NoScreenshot\Events\ScreenRecordingStopped; use Codingwithrk\NoScreenshot\Events\ScreenshotAttempted; // In a controller action polled by the frontend: $status = NoScreenshot::getStatus(); match (true) { $status->isScreenBeingRecorded => ScreenRecordingStarted::dispatch(), default => ScreenRecordingStopped::dispatch(), }; ScreenshotAttempted::dispatch();
ScreenProtectionStatus Reference
| Property | Type | Android | iOS |
|---|---|---|---|
isGloballyProtected |
bool |
✅ | ✅ |
isScreenBeingRecorded |
bool |
Always false |
Live UIScreen.main.isCaptured |
isScreenshotDetectionActive |
bool |
API 34+ only | ✅ All versions |
Platform Notes
Android — min_sdk_version: 21
FLAG_SECUREhas been available since API 1, but NativePHP Mobile itself targets API 21+.- The flag covers the entire activity window — all capture attempts (screenshot button, built-in recorder, ADB, third-party apps) receive a blank frame.
- The flag also hides the app thumbnail in the Recents / App Switcher.
- Screenshot detection via
registerScreenCaptureCallbackrequires API 34+. On older devices,startScreenshotDetection()returnssupported: falseand no events fire.
iOS — min_version: 13.0
- iOS 13 is the minimum because the recording overlay uses
UIWindowScene, which was introduced in iOS 13. UIScreen.main.isCaptured(available iOS 11+) istrueduring screen recording and AirPlay mirroring — the overlay appears in both cases.- Screenshots cannot be prevented at the application level. The image is saved to Photos before the notification fires. The plugin can detect attempts via
UIApplication.userDidTakeScreenshotNotificationand dispatchScreenshotAttempted, but the file is already saved by then. - The black overlay is a
UIWindowatUIWindow.Level.statusBar + 1, placed above all app content including the NativePHP WebView.
Testing
Device / simulator testing
# Install in your NativePHP app (uses path repository) composer require codingwithrk/no-screenshot # Run on Android php artisan native:run android # Run on iOS php artisan native:run ios
Then trigger NoScreenshot::disableGlobally() from a controller or Livewire component and:
Android — press the screenshot button. The OS shows "Can't take screenshot due to security policy."
iOS — start a screen recording from Control Center. The black overlay should appear immediately and disappear when recording stops.
Support
For questions or issues, email connect@codingwithrk.com
License
The MIT License (MIT). Please see License File for more information.