1// Copyright 2018-present 650 Industries. All rights reserved.
2
3#import <EXScreenCapture/EXScreenCaptureModule.h>
4
5#import <ExpoModulesCore/EXEventEmitterService.h>
6
7static NSString * const onScreenshotEventName = @"onScreenshot";
8
9@interface EXScreenCaptureModule ()
10
11@property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
12@property (nonatomic, assign) BOOL isListening;
13@property (nonatomic, assign) BOOL isBeingObserved;
14@property (nonatomic, weak) id<EXEventEmitterService> eventEmitter;
15
16@end
17
18@implementation EXScreenCaptureModule {
19  UIView *_blockView;
20}
21
22EX_EXPORT_MODULE(ExpoScreenCapture);
23
24# pragma mark - EXModuleRegistryConsumer
25
26- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
27{
28  _moduleRegistry = moduleRegistry;
29  _eventEmitter = [moduleRegistry getModuleImplementingProtocol:@protocol(EXEventEmitterService)];
30}
31
32- (instancetype)init {
33  if (self = [super init]) {
34    CGFloat boundLength = MAX([[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
35    _blockView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, boundLength, boundLength)];
36    _blockView.backgroundColor = UIColor.blackColor;
37  }
38  return self;
39}
40
41# pragma mark - Exported methods
42
43EX_EXPORT_METHOD_AS(preventScreenCapture,
44                    preventScreenCaptureWithResolver:(EXPromiseResolveBlock)resolve
45                    reject:(EXPromiseRejectBlock)reject)
46{
47  if (@available(iOS 11.0, *) ) {
48    // If already recording, block it
49    dispatch_async(dispatch_get_main_queue(), ^{
50      [self preventScreenRecording];
51    });
52
53    // Avoid setting duplicate observers
54    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIScreenCapturedDidChangeNotification object:nil];
55
56    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(preventScreenRecording) name:UIScreenCapturedDidChangeNotification object:nil];
57  }
58
59  resolve([NSNull null]);
60}
61
62EX_EXPORT_METHOD_AS(allowScreenCapture,
63                    allowScreenCaptureWithResolver:(EXPromiseResolveBlock)resolve
64                    rejecter:(EXPromiseRejectBlock)reject)
65{
66  if (@available(iOS 11.0, *)) {
67    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIScreenCapturedDidChangeNotification object:nil];
68  }
69
70  resolve([NSNull null]);
71}
72
73- (void)preventScreenRecording {
74  if (@available(iOS 11.0, *)) {
75    BOOL isCaptured = [[UIScreen mainScreen] isCaptured];
76
77    if (isCaptured) {
78      [UIApplication.sharedApplication.keyWindow.subviews.firstObject addSubview:_blockView];
79    } else {
80      [_blockView removeFromSuperview];
81    }
82  }
83}
84
85# pragma mark - EXEventEmitter
86
87- (NSArray<NSString *> *)supportedEvents
88{
89  return @[onScreenshotEventName];
90}
91
92- (void)startObserving
93{
94  [self setIsBeingObserved:YES];
95}
96
97- (void)stopObserving
98{
99  [self setIsBeingObserved:NO];
100}
101
102- (void)setIsBeingObserved:(BOOL)isBeingObserved
103{
104  _isBeingObserved = isBeingObserved;
105  BOOL shouldListen = _isBeingObserved;
106  if (shouldListen && !_isListening) {
107    // Avoid setting duplicate observers
108    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationUserDidTakeScreenshotNotification object:nil];
109    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(listenForScreenCapture) name:UIApplicationUserDidTakeScreenshotNotification object:nil];
110    _isListening = YES;
111  } else if (!shouldListen && _isListening) {
112    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationUserDidTakeScreenshotNotification object:nil];
113    _isListening = NO;
114  }
115}
116
117- (void)listenForScreenCapture
118{
119  [_eventEmitter sendEventWithName:onScreenshotEventName body:nil];
120}
121
122@end
123