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