1 // Copyright 2015-present 650 Industries. All rights reserved. 2 import SafariServices 3 4 @objc(DevMenuInternalModule) 5 public class DevMenuInternalModule: NSObject, RCTBridgeModule { 6 @objc 7 var redirectResolve: RCTPromiseResolveBlock? 8 @objc 9 var redirectReject: RCTPromiseRejectBlock? 10 @objc 11 var authSession: SFAuthenticationSession? 12 13 public static func moduleName() -> String! { 14 return "ExpoDevMenuInternal" 15 } 16 17 // Module DevMenuInternalModule requires main queue setup since it overrides `constantsToExport`. 18 public static func requiresMainQueueSetup() -> Bool { 19 return true; 20 } 21 22 private static var fontsWereLoaded = false; 23 24 let manager: DevMenuManager 25 26 init(manager: DevMenuManager) { 27 self.manager = manager 28 } 29 30 // MARK: JavaScript API 31 32 @objc 33 func constantsToExport() -> [String : Any] { 34 #if targetEnvironment(simulator) 35 let doesDeviceSupportKeyCommands = true 36 #else 37 let doesDeviceSupportKeyCommands = false 38 #endif 39 return ["doesDeviceSupportKeyCommands": doesDeviceSupportKeyCommands] 40 } 41 42 @objc 43 func loadFontsAsync(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 44 if (DevMenuInternalModule.fontsWereLoaded) { 45 resolve(nil); 46 return; 47 } 48 49 let fonts = ["MaterialCommunityIcons", "Ionicons"] 50 for font in fonts { 51 guard let path = DevMenuUtils.resourcesBundle()?.path(forResource: font, ofType: "ttf") else { 52 reject("ERR_DEVMENU_CANNOT_FIND_FONT", "Font file for '\(font)' doesn't exist.", nil); 53 return; 54 } 55 guard let data = FileManager.default.contents(atPath: path) else { 56 reject("ERR_DEVMENU_CANNOT_OPEN_FONT_FILE", "Could not open '\(path)'.", nil); 57 return; 58 } 59 60 guard let provider = CGDataProvider(data: data as CFData) else { 61 reject("ERR_DEVMENU_CANNOT_CREATE_FONT_PROVIDER", "Could not create font provider for '\(font)'.", nil); 62 return; 63 } 64 guard let cgFont = CGFont(provider) else { 65 reject("ERR_DEVMENU_CANNOT_CREATE_FONT", "Could not create font for '\(font)'.", nil); 66 return; 67 } 68 69 var error: Unmanaged<CFError>? 70 if !CTFontManagerRegisterGraphicsFont(cgFont, &error) { 71 reject("ERR_DEVMENU_CANNOT_ADD_FONT", "Could not create font from loaded data for '\(font)'. '\(error.debugDescription)'.", nil) 72 return 73 } 74 } 75 76 DevMenuInternalModule.fontsWereLoaded = true 77 resolve(nil) 78 } 79 80 @objc 81 func dispatchActionAsync(_ actionId: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 82 if actionId == nil { 83 return reject("ERR_DEVMENU_ACTION_FAILED", "Action ID not provided.", nil) 84 } 85 manager.dispatchAction(withId: actionId) 86 resolve(nil) 87 } 88 89 @objc 90 func hideMenu() { 91 manager.hideMenu() 92 } 93 94 @objc 95 func setOnboardingFinished(_ finished: Bool) { 96 DevMenuSettings.isOnboardingFinished = finished 97 } 98 99 @objc 100 func getSettingsAsync(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 101 resolve(DevMenuSettings.serialize()) 102 } 103 104 @objc 105 func setSettingsAsync(_ dict: [String: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 106 if let motionGestureEnabled = dict["motionGestureEnabled"] as? Bool { 107 DevMenuSettings.motionGestureEnabled = motionGestureEnabled 108 } 109 if let touchGestureEnabled = dict["touchGestureEnabled"] as? Bool { 110 DevMenuSettings.touchGestureEnabled = touchGestureEnabled 111 } 112 if let keyCommandsEnabled = dict["keyCommandsEnabled"] as? Bool { 113 DevMenuSettings.keyCommandsEnabled = keyCommandsEnabled 114 } 115 if let showsAtLaunch = dict["showsAtLaunch"] as? Bool { 116 DevMenuSettings.showsAtLaunch = showsAtLaunch 117 } 118 } 119 120 @objc 121 func openDevMenuFromReactNative() { 122 guard let rctDevMenu = manager.session?.bridge.devMenu else { 123 return 124 } 125 126 DispatchQueue.main.async { 127 rctDevMenu.show() 128 } 129 } 130 131 @objc 132 func onScreenChangeAsync(_ currentScreen: String?, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 133 manager.setCurrentScreen(currentScreen) 134 resolve(nil) 135 } 136 137 @objc 138 func saveAsync(_ key: String, data: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 139 UserDefaults.standard.set(data, forKey: key) 140 resolve(nil) 141 } 142 143 @objc 144 func getAsync(_ key: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { 145 resolve(UserDefaults.standard.string(forKey: key)) 146 } 147 } 148