From fe64909746fe575f9a48de797d7d964e6fe462a0 Mon Sep 17 00:00:00 2001 From: zcating Date: Thu, 16 Jan 2025 10:30:07 +0800 Subject: [PATCH] feat: add screen capture feature on iOS --- ios/Detector.m | 37 ++++++++++++++++++--- src/index.tsx | 59 ++------------------------------- src/screen-captured-detector.ts | 46 +++++++++++++++++++++++++ src/screen-shot-dector.ts | 58 ++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 src/screen-captured-detector.ts create mode 100644 src/screen-shot-dector.ts diff --git a/ios/Detector.m b/ios/Detector.m index 6b5a80f..004b934 100644 --- a/ios/Detector.m +++ b/ios/Detector.m @@ -1,19 +1,34 @@ #import "Detector.h" -@implementation Detector +@implementation Detector { + bool isCapturedNotificationSent; +} RCT_EXPORT_MODULE(); - (NSArray *)supportedEvents { - return @[@"UIApplicationUserDidTakeScreenshotNotification"]; + return @[ + @"UIApplicationUserDidTakeScreenshotNotification", + @"UIScreenCapturedDidChangeNotification" + ]; } - (void)startObserving { NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self - selector:@selector(sendNotificationToRN:) - name:UIApplicationUserDidTakeScreenshotNotification - object:nil]; + selector:@selector(sendNotificationToRN:) + name:UIApplicationUserDidTakeScreenshotNotification + object:nil]; + + [center addObserver:self + selector:@selector(sendScreenCapturedNotification:) + name:UIScreenCapturedDidChangeNotification + object:nil]; + + [center addObserver:self + selector:@selector(sendScreenCapturedNotification:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; } @@ -26,4 +41,16 @@ - (void)sendNotificationToRN:(NSNotification *)notification { body:nil]; } +- (void)sendScreenCapturedNotification:(NSNotification *)notification { + if (![UIScreen mainScreen].isCaptured) { + isCapturedNotificationSent = NO; + return; + } + if (isCapturedNotificationSent) { + return; + } + isCapturedNotificationSent = YES; + [self sendEventWithName:@"UIScreenCapturedDidChangeNotification" body:nil]; +} + @end diff --git a/src/index.tsx b/src/index.tsx index 9c8ca3f..61d1e26 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,57 +1,2 @@ -import { NativeModules, NativeEventEmitter, Platform } from 'react-native'; - -const { Detector } = NativeModules; - -enum EventsName { - UserDidTakeScreenshot = 'UIApplicationUserDidTakeScreenshotNotification', -} - -const detectorEventEmitter = new NativeEventEmitter(Detector); - -type Unsubscribe = () => void; - -const commonAddScreenshotListener = (listener: () => void): Unsubscribe => { - const eventSubscription = detectorEventEmitter.addListener( - EventsName.UserDidTakeScreenshot, - () => listener(), - {} - ); - - return () => { - eventSubscription.remove(); - }; -}; - -const getListenersCount = (): number => { - return ( - // React Native 0.64+ - // @ts-ignore - detectorEventEmitter.listenerCount?.(EventsName.UserDidTakeScreenshot) ?? - // React Native < 0.64 - // @ts-ignore - detectorEventEmitter.listeners?.(EventsName.UserDidTakeScreenshot).length ?? - 0 - ); -}; - -export const addScreenshotListener = Platform.select< - (listener: () => void) => Unsubscribe ->({ - default: (): Unsubscribe => () => {}, - ios: commonAddScreenshotListener, - android: (listener: () => void): Unsubscribe => { - if (getListenersCount() === 0) { - Detector.startScreenshotDetection(); - } - - const unsubscribe: Unsubscribe = commonAddScreenshotListener(listener); - - return () => { - unsubscribe(); - - if (getListenersCount() === 0) { - Detector.stopScreenshotDetection(); - } - }; - }, -}); +export { addScreenshotListener } from './screen-shot-dector'; +export { addScreenCapturedListener } from './screen-captured-detector'; diff --git a/src/screen-captured-detector.ts b/src/screen-captured-detector.ts new file mode 100644 index 0000000..9565861 --- /dev/null +++ b/src/screen-captured-detector.ts @@ -0,0 +1,46 @@ +import { NativeModules, NativeEventEmitter, Platform } from 'react-native'; + +const { Detector } = NativeModules; + +enum EventsName { + UserDidCapturedScreen = "UIScreenCapturedDidChangeNotification" +} + +const detectorEventEmitter = new NativeEventEmitter(Detector); + +type Unsubscribe = () => void; + +const commonAddScreenCapturedListener = (listener: () => void): Unsubscribe => { + const eventSubscription = detectorEventEmitter.addListener( + EventsName.UserDidCapturedScreen, + () => listener(), + {} + ); + + return () => { + eventSubscription.remove(); + }; +}; + +// const getListenersCount = (): number => { +// return ( +// // React Native 0.64+ +// // @ts-ignore +// detectorEventEmitter.listenerCount?.(EventsName.UserDidCapturedScreen) ?? +// // React Native < 0.64 +// // @ts-ignore +// detectorEventEmitter.listeners?.(EventsName.UserDidCapturedScreen).length ?? +// 0 +// ); +// }; + +export const addScreenCapturedListener = Platform.select< + (listener: () => void) => Unsubscribe +>({ + default: (): Unsubscribe => () => {}, + ios: commonAddScreenCapturedListener, + android: (listener: () => void): Unsubscribe => { + // TODO: add Android screen capture support + return () => {}; + }, +}); diff --git a/src/screen-shot-dector.ts b/src/screen-shot-dector.ts new file mode 100644 index 0000000..e940936 --- /dev/null +++ b/src/screen-shot-dector.ts @@ -0,0 +1,58 @@ +import { NativeModules, NativeEventEmitter, Platform } from 'react-native'; + +const { Detector } = NativeModules; + +enum EventsName { + UserDidTakeScreenshot = 'UIApplicationUserDidTakeScreenshotNotification', + UserDidCapturedScreen = "UIScreenCapturedDidChangeNotification" +} + +const detectorEventEmitter = new NativeEventEmitter(Detector); + +type Unsubscribe = () => void; + +const commonAddScreenshotListener = (listener: () => void): Unsubscribe => { + const eventSubscription = detectorEventEmitter.addListener( + EventsName.UserDidTakeScreenshot, + () => listener(), + {} + ); + + return () => { + eventSubscription.remove(); + }; +}; + +const getListenersCount = (): number => { + return ( + // React Native 0.64+ + // @ts-ignore + detectorEventEmitter.listenerCount?.(EventsName.UserDidTakeScreenshot) ?? + // React Native < 0.64 + // @ts-ignore + detectorEventEmitter.listeners?.(EventsName.UserDidTakeScreenshot).length ?? + 0 + ); +}; + +export const addScreenshotListener = Platform.select< + (listener: () => void) => Unsubscribe +>({ + default: (): Unsubscribe => () => {}, + ios: commonAddScreenshotListener, + android: (listener: () => void): Unsubscribe => { + if (getListenersCount() === 0) { + Detector.startScreenshotDetection(); + } + + const unsubscribe: Unsubscribe = commonAddScreenshotListener(listener); + + return () => { + unsubscribe(); + + if (getListenersCount() === 0) { + Detector.stopScreenshotDetection(); + } + }; + }, +});