1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 @objc(DevMenuInternalModule)
4 public class DevMenuInternalModule: NSObject, RCTBridgeModule {
5   public static func moduleName() -> String! {
6     return "ExpoDevMenuInternal"
7   }
8 
9   private static var fontsWereLoaded = false;
10 
11   let manager: DevMenuManager
12 
13   init(manager: DevMenuManager) {
14     self.manager = manager
15   }
16 
17   // MARK: JavaScript API
18 
19   @objc
20   func loadFontsAsync(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
21     if (DevMenuInternalModule.fontsWereLoaded) {
22       resolve(nil);
23       return;
24     }
25 
26     let fonts = ["MaterialCommunityIcons", "Ionicons"]
27     for font in fonts {
28       guard let path = DevMenuUtils.resourcesBundle()?.path(forResource: font, ofType: "ttf") else {
29         reject("ERR_DEVMENU_CANNOT_FIND_FONT", "Font file for '\(font)' doesn't exist.", nil);
30         return;
31       }
32       guard let data = FileManager.default.contents(atPath: path) else {
33         reject("ERR_DEVMENU_CANNOT_OPEN_FONT_FILE", "Could not open '\(path)'.", nil);
34         return;
35       }
36 
37       guard let provider = CGDataProvider(data: data as CFData) else {
38         reject("ERR_DEVMENU_CANNOT_CREATE_FONT_PROVIDER", "Could not create font provider for '\(font)'.", nil);
39         return;
40       }
41       guard let cgFont = CGFont(provider) else {
42         reject("ERR_DEVMENU_CANNOT_CREATE_FONT", "Could not create font for '\(font)'.", nil);
43         return;
44       }
45 
46       var error: Unmanaged<CFError>?
47       if !CTFontManagerRegisterGraphicsFont(cgFont, &error) {
48         reject("ERR_DEVMENU_CANNOT_ADD_FONT", "Could not create font from loaded data for '\(font)'. '\(error.debugDescription)'.", nil)
49         return
50       }
51     }
52 
53     DevMenuInternalModule.fontsWereLoaded = true
54     resolve(nil)
55   }
56 
57   @objc
58   func dispatchActionAsync(_ actionId: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
59     if actionId == nil {
60       return reject("ERR_DEVMENU_ACTION_FAILED", "Action ID not provided.", nil)
61     }
62     manager.dispatchAction(withId: actionId)
63     resolve(nil)
64   }
65 
66   @objc
67   func hideMenu() {
68     manager.hideMenu()
69   }
70 
71   @objc
72   func setOnboardingFinished(_ finished: Bool) {
73     DevMenuSettings.isOnboardingFinished = finished
74   }
75 
76   @objc
77   func getSettingsAsync(_ resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
78     resolve(DevMenuSettings.serialize())
79   }
80 
81   @objc
82   func setSettingsAsync(_ dict: [String: Any], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
83     if let motionGestureEnabled = dict["motionGestureEnabled"] as? Bool {
84       DevMenuSettings.motionGestureEnabled = motionGestureEnabled
85     }
86     if let touchGestureEnabled = dict["touchGestureEnabled"] as? Bool {
87       DevMenuSettings.touchGestureEnabled = touchGestureEnabled
88     }
89     if let keyCommandsEnabled = dict["keyCommandsEnabled"] as? Bool {
90       DevMenuSettings.keyCommandsEnabled = keyCommandsEnabled
91     }
92     if let showsAtLaunch = dict["showsAtLaunch"] as? Bool {
93       DevMenuSettings.showsAtLaunch = showsAtLaunch
94     }
95   }
96 }
97