1 import ABI49_0_0ExpoModulesCore
2 
3 public class ScreenOrientationModule: Module, ScreenOrientationController {
4   static let didUpdateDimensionsEvent = "expoDidUpdateDimensions"
5 
6   let screenOrientationRegistry = ScreenOrientationRegistry.shared
7   var shouldEmitEvents = false
8 
9   public func definition() -> ModuleDefinition {
10     Name("ExpoScreenOrientation")
11 
12     Events("expoDidUpdateDimensions")
13 
14     AsyncFunction("lockAsync") { (orientationLock: ModuleOrientationLock) in
15       let orientationMask = orientationLock.toInterfaceOrientationMask()
16 
17       guard !orientationMask.isEmpty else {
18         throw InvalidOrientationLockException()
19       }
20 
21       guard orientationMask.isSupportedByDevice() else {
22         throw UnsupportedOrientationLockException(orientationLock)
23       }
24 
25       screenOrientationRegistry.setMask(orientationMask, forController: self)
26     }
27 
28     AsyncFunction("lockPlatformAsync") { (allowedOrientations: [ModuleOrientation]) in
29       var allowedOrientationsMask: UIInterfaceOrientationMask = []
30 
31       for allowedOrientation in allowedOrientations {
32         let orientation = allowedOrientation.toInterfaceOrientation()
33         let orientationMask = orientation.toInterfaceOrientationMask()
34 
35         guard !orientationMask.isEmpty else {
36           throw InvalidOrientationLockException()
37         }
38 
39         allowedOrientationsMask.insert(orientationMask)
40       }
41 
42       guard allowedOrientationsMask.isSupportedByDevice() else {
43         throw UnsupportedOrientationLockException(nil)
44       }
45 
46       screenOrientationRegistry.setMask(allowedOrientationsMask, forController: self)
47     }
48 
49     AsyncFunction("getOrientationLockAsync") {
50       return ModuleOrientationLock.from(mask: screenOrientationRegistry.currentOrientationMask).rawValue
51     }
52 
53     AsyncFunction("getPlatformOrientationLockAsync") { () -> [Int] in
54       let orientationMask = screenOrientationRegistry.currentOrientationMask
55       var allowedOrientations: [Int] = []
56       let orientationMasks: [UIInterfaceOrientationMask] = [.portrait, .portraitUpsideDown, .landscapeLeft, .landscapeRight]
57 
58       // If the particular orientation is supported, we add it to the array of allowedOrientations
59       for wrappedSingleOrientation in orientationMasks {
60         let supportedOrientationMask = orientationMask.intersection(wrappedSingleOrientation)
61         if !supportedOrientationMask.isEmpty {
62           let supportedOrientation = supportedOrientationMask.toUIInterfaceOrientation()
63           allowedOrientations.append(ModuleOrientation.from(orientation: supportedOrientation).rawValue)
64         }
65       }
66       return allowedOrientations
67     }
68 
69     AsyncFunction("supportsOrientationLockAsync") { (orientationLock: ModuleOrientationLock) -> Bool in
70       let orientationMask = orientationLock.toInterfaceOrientationMask()
71       return !orientationMask.isEmpty && orientationMask.isSupportedByDevice()
72     }
73 
74     AsyncFunction("getOrientationAsync") {
75       return ModuleOrientation.from(orientation: screenOrientationRegistry.currentScreenOrientation).rawValue
76     }
77 
78     OnCreate {
79       screenOrientationRegistry.registerController(self)
80     }
81 
82     OnDestroy {
83       screenOrientationRegistry.unregisterController(self)
84     }
85 
86     OnStartObserving {
87       shouldEmitEvents = true
88     }
89 
90     OnStopObserving {
91       shouldEmitEvents = false
92     }
93   }
94 
95   // MARK: - ScreenOrientationController
96 
97   public func screenOrientationDidChange(_ orientation: UIInterfaceOrientation) {
98     guard let currentTraitCollection = screenOrientationRegistry.currentTraitCollection, shouldEmitEvents else {
99       return
100     }
101 
102     sendEvent(ScreenOrientationModule.didUpdateDimensionsEvent, [
103       "orientationLock": ModuleOrientationLock.from(mask: screenOrientationRegistry.currentOrientationMask).rawValue,
104       "orientationInfo": [
105         "orientation": ModuleOrientation.from(orientation: orientation).rawValue,
106         "verticalSizeClass": currentTraitCollection.verticalSizeClass,
107         "horizontalSizeClass": currentTraitCollection.horizontalSizeClass
108       ] as [String: Any]
109     ])
110   }
111 }
112