1*f67462bcSTomasz Sapeta// Copyright 2015-present 650 Industries. All rights reserved.
2*f67462bcSTomasz Sapeta
3*f67462bcSTomasz Sapeta#import <React/RCTUtils.h>
4*f67462bcSTomasz Sapeta
5*f67462bcSTomasz Sapeta#import "EXDevMenuGestureInterceptor.h"
6*f67462bcSTomasz Sapeta#import "EXDevMenuGestureRecognizer.h"
7*f67462bcSTomasz Sapeta
8*f67462bcSTomasz Sapeta@import UIKit;
9*f67462bcSTomasz Sapeta
10*f67462bcSTomasz Sapetastatic BOOL isInstalled = NO;
11*f67462bcSTomasz Sapetastatic EXDevMenuGestureRecognizer *gestureRecognizerInstance;
12*f67462bcSTomasz Sapeta
13*f67462bcSTomasz Sapeta@implementation UIWindow (EXDevMenuGestureInterceptor)
14*f67462bcSTomasz Sapeta
15*f67462bcSTomasz Sapeta- (NSArray<__kindof UIGestureRecognizer *> *)EX_gestureRecognizers
16*f67462bcSTomasz Sapeta{
17*f67462bcSTomasz Sapeta  // Just for thread safety, someone may uninstall the interceptor in the meantime and we would fall into recursive loop.
18*f67462bcSTomasz Sapeta  if (!isInstalled) {
19*f67462bcSTomasz Sapeta    return [self gestureRecognizers];
20*f67462bcSTomasz Sapeta  }
21*f67462bcSTomasz Sapeta
22*f67462bcSTomasz Sapeta  // Check for the case where singleton instance of gesture recognizer is not created yet or is attached to different window.
23*f67462bcSTomasz Sapeta  if (!gestureRecognizerInstance || gestureRecognizerInstance.view != self) {
24*f67462bcSTomasz Sapeta    // Create gesture recognizer if not created yet.
25*f67462bcSTomasz Sapeta    static dispatch_once_t onceToken;
26*f67462bcSTomasz Sapeta    dispatch_once(&onceToken, ^{
27*f67462bcSTomasz Sapeta      gestureRecognizerInstance = [EXDevMenuGestureRecognizer new];
28*f67462bcSTomasz Sapeta    });
29*f67462bcSTomasz Sapeta
30*f67462bcSTomasz Sapeta    // Remove it from the window it's attached to.
31*f67462bcSTomasz Sapeta    [gestureRecognizerInstance.view removeGestureRecognizer:gestureRecognizerInstance];
32*f67462bcSTomasz Sapeta
33*f67462bcSTomasz Sapeta    // Attach to this window.
34*f67462bcSTomasz Sapeta    [self addGestureRecognizer:gestureRecognizerInstance];
35*f67462bcSTomasz Sapeta  }
36*f67462bcSTomasz Sapeta
37*f67462bcSTomasz Sapeta  // `EX_gestureRecognizers` implementation has been swapped with `gestureRecognizers` - it might be confusing that we call it recursively, but we don't.
38*f67462bcSTomasz Sapeta  return [self EX_gestureRecognizers];
39*f67462bcSTomasz Sapeta}
40*f67462bcSTomasz Sapeta
41*f67462bcSTomasz Sapeta@end
42*f67462bcSTomasz Sapeta
43*f67462bcSTomasz Sapeta@implementation EXDevMenuGestureInterceptor
44*f67462bcSTomasz Sapeta
45*f67462bcSTomasz Sapeta+ (void)install
46*f67462bcSTomasz Sapeta{
47*f67462bcSTomasz Sapeta  if (!isInstalled) {
48*f67462bcSTomasz Sapeta    // Capture touch gesture from any window by swapping default implementation from UIWindow.
49*f67462bcSTomasz Sapeta    RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers));
50*f67462bcSTomasz Sapeta    isInstalled = YES;
51*f67462bcSTomasz Sapeta
52*f67462bcSTomasz Sapeta    // Make sure gesture recognizer is enabled.
53*f67462bcSTomasz Sapeta    [gestureRecognizerInstance setEnabled:YES];
54*f67462bcSTomasz Sapeta  }
55*f67462bcSTomasz Sapeta}
56*f67462bcSTomasz Sapeta
57*f67462bcSTomasz Sapeta+ (void)uninstall
58*f67462bcSTomasz Sapeta{
59*f67462bcSTomasz Sapeta  if (isInstalled) {
60*f67462bcSTomasz Sapeta    // Bring back the original method.
61*f67462bcSTomasz Sapeta    RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers));
62*f67462bcSTomasz Sapeta    isInstalled = NO;
63*f67462bcSTomasz Sapeta
64*f67462bcSTomasz Sapeta    // Cancel recognized gestures and disable it entirely.
65*f67462bcSTomasz Sapeta    [gestureRecognizerInstance setEnabled:NO];
66*f67462bcSTomasz Sapeta  }
67*f67462bcSTomasz Sapeta}
68*f67462bcSTomasz Sapeta
69*f67462bcSTomasz Sapeta+ (BOOL)isInstalled
70*f67462bcSTomasz Sapeta{
71*f67462bcSTomasz Sapeta  return isInstalled;
72*f67462bcSTomasz Sapeta}
73*f67462bcSTomasz Sapeta
74*f67462bcSTomasz Sapeta@end
75