1*f67462bcSTomasz Sapeta// Copyright 2015-present 650 Industries. All rights reserved. 2*f67462bcSTomasz Sapeta 3*f67462bcSTomasz Sapeta#import "EXDevMenuManager.h" 4*f67462bcSTomasz Sapeta#import "EXDevMenuWindow.h" 5*f67462bcSTomasz Sapeta#import "EXHomeModule.h" 6*f67462bcSTomasz Sapeta#import "EXDevMenuMotionInterceptor.h" 7*f67462bcSTomasz Sapeta#import "EXDevMenuGestureInterceptor.h" 8*f67462bcSTomasz Sapeta 9*f67462bcSTomasz Sapetastatic NSString *kEXDevMenuMotionGestureEnabled = @"EXDevMenuMotionGestureEnabled"; 10*f67462bcSTomasz Sapetastatic NSString *kEXDevMenuTouchGestureEnabled = @"EXDevMenuTouchGestureEnabled"; 11*f67462bcSTomasz Sapeta 12*f67462bcSTomasz Sapeta@interface EXDevMenuManager () 13*f67462bcSTomasz Sapeta 14*f67462bcSTomasz Sapeta@property (nonatomic, strong) EXDevMenuWindow *window; 15*f67462bcSTomasz Sapeta 16*f67462bcSTomasz Sapeta@end 17*f67462bcSTomasz Sapeta 18*f67462bcSTomasz Sapeta@implementation EXDevMenuManager 19*f67462bcSTomasz Sapeta 20*f67462bcSTomasz Sapeta+ (instancetype)sharedInstance 21*f67462bcSTomasz Sapeta{ 22*f67462bcSTomasz Sapeta static EXDevMenuManager *manager; 23*f67462bcSTomasz Sapeta static dispatch_once_t once; 24*f67462bcSTomasz Sapeta dispatch_once(&once, ^{ 25*f67462bcSTomasz Sapeta if (!manager) { 26*f67462bcSTomasz Sapeta manager = [EXDevMenuManager new]; 27*f67462bcSTomasz Sapeta 28*f67462bcSTomasz Sapeta // Read initial attributes from user defaults. 29*f67462bcSTomasz Sapeta id motionGestureEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:kEXDevMenuMotionGestureEnabled]; 30*f67462bcSTomasz Sapeta id touchGestureEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:kEXDevMenuTouchGestureEnabled]; 31*f67462bcSTomasz Sapeta manager.interceptMotionGesture = motionGestureEnabled != nil ? [motionGestureEnabled boolValue] : YES; 32*f67462bcSTomasz Sapeta manager.interceptTouchGesture = touchGestureEnabled != nil ? [touchGestureEnabled boolValue] : YES; 33*f67462bcSTomasz Sapeta } 34*f67462bcSTomasz Sapeta }); 35*f67462bcSTomasz Sapeta return manager; 36*f67462bcSTomasz Sapeta} 37*f67462bcSTomasz Sapeta 38*f67462bcSTomasz Sapeta#pragma mark - API 39*f67462bcSTomasz Sapeta 40*f67462bcSTomasz Sapeta- (BOOL)interceptMotionGesture 41*f67462bcSTomasz Sapeta{ 42*f67462bcSTomasz Sapeta return [EXDevMenuMotionInterceptor isInstalled]; 43*f67462bcSTomasz Sapeta} 44*f67462bcSTomasz Sapeta 45*f67462bcSTomasz Sapeta- (void)setInterceptMotionGesture:(BOOL)interceptMotionGesture 46*f67462bcSTomasz Sapeta{ 47*f67462bcSTomasz Sapeta interceptMotionGesture ? [EXDevMenuMotionInterceptor install] : [EXDevMenuMotionInterceptor uninstall]; 48*f67462bcSTomasz Sapeta [[NSUserDefaults standardUserDefaults] setBool:interceptMotionGesture forKey:kEXDevMenuMotionGestureEnabled]; 49*f67462bcSTomasz Sapeta} 50*f67462bcSTomasz Sapeta 51*f67462bcSTomasz Sapeta- (BOOL)interceptTouchGesture 52*f67462bcSTomasz Sapeta{ 53*f67462bcSTomasz Sapeta return [EXDevMenuGestureInterceptor isInstalled]; 54*f67462bcSTomasz Sapeta} 55*f67462bcSTomasz Sapeta 56*f67462bcSTomasz Sapeta- (void)setInterceptTouchGesture:(BOOL)interceptTouchGesture 57*f67462bcSTomasz Sapeta{ 58*f67462bcSTomasz Sapeta interceptTouchGesture ? [EXDevMenuGestureInterceptor install] : [EXDevMenuGestureInterceptor uninstall]; 59*f67462bcSTomasz Sapeta [[NSUserDefaults standardUserDefaults] setBool:interceptTouchGesture forKey:kEXDevMenuTouchGestureEnabled]; 60*f67462bcSTomasz Sapeta} 61*f67462bcSTomasz Sapeta 62*f67462bcSTomasz Sapeta- (RCTBridge *)mainBridge 63*f67462bcSTomasz Sapeta{ 64*f67462bcSTomasz Sapeta return [_delegate mainBridgeForDevMenuManager:self]; 65*f67462bcSTomasz Sapeta} 66*f67462bcSTomasz Sapeta 67*f67462bcSTomasz Sapeta- (BOOL)isVisible 68*f67462bcSTomasz Sapeta{ 69*f67462bcSTomasz Sapeta return _window ? !_window.hidden : NO; 70*f67462bcSTomasz Sapeta} 71*f67462bcSTomasz Sapeta 72*f67462bcSTomasz Sapeta- (BOOL)open 73*f67462bcSTomasz Sapeta{ 74*f67462bcSTomasz Sapeta if (![self canChangeVisibility:YES]) { 75*f67462bcSTomasz Sapeta return NO; 76*f67462bcSTomasz Sapeta } 77*f67462bcSTomasz Sapeta [self setVisibility:YES]; 78*f67462bcSTomasz Sapeta return YES; 79*f67462bcSTomasz Sapeta} 80*f67462bcSTomasz Sapeta 81*f67462bcSTomasz Sapeta- (BOOL)close 82*f67462bcSTomasz Sapeta{ 83*f67462bcSTomasz Sapeta if (![self canChangeVisibility:NO]) { 84*f67462bcSTomasz Sapeta return NO; 85*f67462bcSTomasz Sapeta } 86*f67462bcSTomasz Sapeta EXHomeModule *homeModule = [[self mainBridge] moduleForName:@"ExponentKernel"]; 87*f67462bcSTomasz Sapeta 88*f67462bcSTomasz Sapeta if (homeModule) { 89*f67462bcSTomasz Sapeta // This will trigger `closeWithoutAnimation` once the animation is finished. 90*f67462bcSTomasz Sapeta [homeModule requestToCloseDevMenu]; 91*f67462bcSTomasz Sapeta } else { 92*f67462bcSTomasz Sapeta // Module not found, close immediately? 93*f67462bcSTomasz Sapeta [self closeWithoutAnimation]; 94*f67462bcSTomasz Sapeta } 95*f67462bcSTomasz Sapeta 96*f67462bcSTomasz Sapeta return YES; 97*f67462bcSTomasz Sapeta} 98*f67462bcSTomasz Sapeta 99*f67462bcSTomasz Sapeta- (BOOL)toggle 100*f67462bcSTomasz Sapeta{ 101*f67462bcSTomasz Sapeta return self.isVisible ? [self close] : [self open]; 102*f67462bcSTomasz Sapeta} 103*f67462bcSTomasz Sapeta 104*f67462bcSTomasz Sapeta- (void)closeWithoutAnimation 105*f67462bcSTomasz Sapeta{ 106*f67462bcSTomasz Sapeta [self setVisibility:NO]; 107*f67462bcSTomasz Sapeta} 108*f67462bcSTomasz Sapeta 109*f67462bcSTomasz Sapeta#pragma mark - delegate stubs 110*f67462bcSTomasz Sapeta 111*f67462bcSTomasz Sapeta- (BOOL)canChangeVisibility:(BOOL)visible 112*f67462bcSTomasz Sapeta{ 113*f67462bcSTomasz Sapeta if (self.isVisible == visible) { 114*f67462bcSTomasz Sapeta return NO; 115*f67462bcSTomasz Sapeta } 116*f67462bcSTomasz Sapeta if ([_delegate respondsToSelector:@selector(devMenuManager:canChangeVisibility:)]) { 117*f67462bcSTomasz Sapeta return [_delegate devMenuManager:self canChangeVisibility:visible]; 118*f67462bcSTomasz Sapeta } 119*f67462bcSTomasz Sapeta return YES; 120*f67462bcSTomasz Sapeta} 121*f67462bcSTomasz Sapeta 122*f67462bcSTomasz Sapeta#pragma mark - internal 123*f67462bcSTomasz Sapeta 124*f67462bcSTomasz Sapeta- (void)setVisibility:(BOOL)visible 125*f67462bcSTomasz Sapeta{ 126*f67462bcSTomasz Sapeta dispatch_async(dispatch_get_main_queue(), ^{ 127*f67462bcSTomasz Sapeta if (!self->_window) { 128*f67462bcSTomasz Sapeta self->_window = [EXDevMenuWindow new]; 129*f67462bcSTomasz Sapeta } 130*f67462bcSTomasz Sapeta if (visible) { 131*f67462bcSTomasz Sapeta [self->_window makeKeyAndVisible]; 132*f67462bcSTomasz Sapeta } else { 133*f67462bcSTomasz Sapeta self->_window.hidden = YES; 134*f67462bcSTomasz Sapeta } 135*f67462bcSTomasz Sapeta }); 136*f67462bcSTomasz Sapeta} 137*f67462bcSTomasz Sapeta 138*f67462bcSTomasz Sapeta@end 139