1 // Copyright 2015-present 650 Industries. All rights reserved. 2 3 let motionGestureEnabledKey = "EXDevMenuMotionGestureEnabled" 4 let touchGestureEnabledKey = "EXDevMenuTouchGestureEnabled" 5 let keyCommandsEnabledKey = "EXDevMenuKeyCommandsEnabled" 6 let showsAtLaunchKey = "EXDevMenuShowsAtLaunch" 7 let isOnboardingFinishedKey = "EXDevMenuIsOnboardingFinished" 8 9 @objc(DevMenuPreferences) 10 public class DevMenuPreferences: NSObject, RCTBridgeModule { 11 /** 12 Initializes dev menu preferences by registering user defaults 13 and applying some preferences to static classes like interceptors. 14 */ 15 static func setup() { 16 UserDefaults.standard.register(defaults: [ 17 motionGestureEnabledKey: true, 18 touchGestureEnabledKey: true, 19 keyCommandsEnabledKey: true, 20 showsAtLaunchKey: false, 21 isOnboardingFinishedKey: false 22 ]) 23 24 /** 25 We don't want to uninstall `DevMenuMotionInterceptor`, because otherwise, the app on shake gesture will bring up the dev-menu from the RN. 26 So we added `isEnabled` to disable it, but not uninstall. 27 */ 28 DevMenuMotionInterceptor.isInstalled = true 29 DevMenuMotionInterceptor.isEnabled = DevMenuPreferences.motionGestureEnabled 30 DevMenuTouchInterceptor.isInstalled = DevMenuPreferences.touchGestureEnabled 31 DevMenuKeyCommandsInterceptor.isInstalled = DevMenuPreferences.keyCommandsEnabled 32 } 33 34 /** 35 Whether to enable shake gesture. 36 */ 37 static var motionGestureEnabled: Bool { 38 get { 39 return boolForKey(motionGestureEnabledKey) 40 } 41 set { 42 setBool(newValue, forKey: motionGestureEnabledKey) 43 DevMenuMotionInterceptor.isEnabled = newValue 44 } 45 } 46 47 /** 48 Whether to enable three-finger long press gesture. 49 */ 50 static var touchGestureEnabled: Bool { 51 get { 52 return boolForKey(touchGestureEnabledKey) 53 } 54 set { 55 setBool(newValue, forKey: touchGestureEnabledKey) 56 DevMenuTouchInterceptor.isInstalled = newValue 57 } 58 } 59 60 /** 61 Whether to enable key commands. 62 */ 63 static var keyCommandsEnabled: Bool { 64 get { 65 return boolForKey(keyCommandsEnabledKey) 66 } 67 set { 68 setBool(newValue, forKey: keyCommandsEnabledKey) 69 DevMenuKeyCommandsInterceptor.isInstalled = newValue 70 } 71 } 72 73 /** 74 Whether to automatically show the dev menu once its delegate is set and the bridge is loaded. 75 */ 76 static var showsAtLaunch: Bool { 77 get { 78 return DevMenuTestInterceptorManager.interceptor?.shouldShowAtLaunch ?? boolForKey(showsAtLaunchKey) 79 } 80 set { 81 setBool(newValue, forKey: showsAtLaunchKey) 82 } 83 } 84 85 /** 86 Returns `true` only if the user finished onboarding, `false` otherwise. 87 */ 88 static var isOnboardingFinished: Bool { 89 get { 90 return DevMenuTestInterceptorManager.interceptor?.isOnboardingFinishedKey ?? boolForKey(isOnboardingFinishedKey) 91 } 92 set { 93 setBool(newValue, forKey: isOnboardingFinishedKey) 94 } 95 } 96 97 /** 98 Serializes settings into a dictionary so they can be passed through the bridge. 99 */ 100 static func serialize() -> [String: Any] { 101 return [ 102 "motionGestureEnabled": DevMenuPreferences.motionGestureEnabled, 103 "touchGestureEnabled": DevMenuPreferences.touchGestureEnabled, 104 "keyCommandsEnabled": DevMenuPreferences.keyCommandsEnabled, 105 "showsAtLaunch": DevMenuPreferences.showsAtLaunch, 106 "isOnboardingFinished": DevMenuPreferences.isOnboardingFinished 107 ] 108 } 109 110 static func setSettings(_ settings: [String: Any]) { 111 if let motionGestureEnabled = settings["motionGestureEnabled"] as? Bool { 112 DevMenuPreferences.motionGestureEnabled = motionGestureEnabled 113 } 114 if let touchGestureEnabled = settings["touchGestureEnabled"] as? Bool { 115 DevMenuPreferences.touchGestureEnabled = touchGestureEnabled 116 } 117 if let keyCommandsEnabled = settings["keyCommandsEnabled"] as? Bool { 118 DevMenuPreferences.keyCommandsEnabled = keyCommandsEnabled 119 } 120 if let showsAtLaunch = settings["showsAtLaunch"] as? Bool { 121 DevMenuPreferences.showsAtLaunch = showsAtLaunch 122 } 123 } 124 125 // MARK - RCTBridgeModule 126 127 public static func moduleName() -> String! { 128 return "DevMenuPreferences" 129 } 130 131 public static func requiresMainQueueSetup() -> Bool { 132 return true 133 } 134 135 @objc 136 func getPreferencesAsync(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 137 resolve(DevMenuPreferences.serialize()) 138 } 139 140 @objc 141 func setPreferencesAsync(_ settings: [String: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 142 DevMenuPreferences.setSettings(settings) 143 resolve(nil) 144 } 145 } 146 147 148 149 private func boolForKey(_ key: String) -> Bool { 150 return UserDefaults.standard.bool(forKey: key) 151 } 152 153 private func setBool(_ value: Bool, forKey key: String) { 154 UserDefaults.standard.set(value, forKey: key) 155 } 156