1// Copyright 2015-present 650 Industries. All rights reserved.
2
3#import "EXKernel.h"
4#import "EXFrame.h"
5#import "EXFrameReactAppManager.h"
6#import "EXKernelReactAppManager.h"
7#import "EXSensorManager.h"
8#import <CoreMotion/CoreMotion.h>
9
10@interface EXSensorManager ()
11
12@property (nonatomic, strong) CMMotionManager *manager;
13@property (nonatomic, strong) NSMutableDictionary *accelerometerHandlers;
14@property (nonatomic, strong) NSMutableDictionary *deviceMotionHandlers;
15@property (nonatomic, strong) NSMutableDictionary *gyroscopeHandlers;
16@property (nonatomic, strong) NSMutableDictionary *magnetometerHandlers;
17@property (nonatomic, strong) NSMutableDictionary *magnetometerUncalibratedHandlers;
18
19@end
20
21@implementation EXSensorManager
22
23- (instancetype)init
24{
25  if (self = [super init]) {
26    _accelerometerHandlers = [[NSMutableDictionary alloc] init];
27    _deviceMotionHandlers = [[NSMutableDictionary alloc] init];
28    _gyroscopeHandlers = [[NSMutableDictionary alloc] init];
29    _magnetometerHandlers = [[NSMutableDictionary alloc] init];
30    _magnetometerUncalibratedHandlers = [[NSMutableDictionary alloc] init];
31  }
32  return self;
33}
34
35- (CMMotionManager *)manager
36{
37  if (!_manager) {
38    _manager = [[CMMotionManager alloc] init];
39  }
40  return _manager;
41}
42
43- (void)dealloc
44{
45  [self.manager stopAccelerometerUpdates];
46  [self.manager stopDeviceMotionUpdates];
47  [self.manager stopGyroUpdates];
48  [self.manager stopMagnetometerUpdates];
49}
50
51- (void)sensorModuleDidSubscribeForAccelerometerUpdates:(id)scopedSensorModule
52                                            withHandler:(void (^)(NSDictionary *event))handlerBlock
53{
54  if ([self.manager isAccelerometerAvailable]) {
55    self.accelerometerHandlers[((EXScopedEventEmitter *)scopedSensorModule).experienceId] = handlerBlock;
56  }
57  if (![self.manager isAccelerometerActive]) {
58    [self.manager setAccelerometerUpdateInterval:0.1f];
59    [self.manager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *data, NSError *error) {
60      for (void (^handler)(NSDictionary *) in self.accelerometerHandlers.allValues) {
61        handler(@{
62                  @"x": [NSNumber numberWithDouble:data.acceleration.x],
63                  @"y": [NSNumber numberWithDouble:data.acceleration.y],
64                  @"z": [NSNumber numberWithDouble:data.acceleration.z]
65                  });
66      }
67    }];
68  }
69}
70
71- (void)sensorModuleDidUnsubscribeForAccelerometerUpdates:(id)scopedSensorModule
72{
73  [self.accelerometerHandlers removeObjectForKey:((EXScopedEventEmitter *)scopedSensorModule).experienceId];
74  if (self.accelerometerHandlers.count == 0) {
75    [self.manager stopAccelerometerUpdates];
76  }
77}
78
79- (void)setAccelerometerUpdateInterval:(NSTimeInterval)intervalMs
80{
81  [self.manager setAccelerometerUpdateInterval:intervalMs];
82}
83
84- (void)sensorModuleDidSubscribeForDeviceMotionUpdates:(id)scopedSensorModule
85                                           withHandler:(void (^)(NSDictionary *event))handlerBlock
86{
87  if ([self.manager isDeviceMotionAvailable]) {
88    self.deviceMotionHandlers[((EXScopedEventEmitter *)scopedSensorModule).experienceId] = handlerBlock;
89  }
90  if (![self.manager isDeviceMotionActive]) {
91    [self activateDeviceMotionUpdates];
92  }
93}
94
95- (void)sensorModuleDidUnsubscribeForDeviceMotionUpdates:(id)scopedSensorModule
96{
97  [self.deviceMotionHandlers removeObjectForKey:((EXScopedEventEmitter *)scopedSensorModule).experienceId];
98  if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) {
99    [self.manager stopDeviceMotionUpdates];
100  }
101}
102
103- (void)setDeviceMotionUpdateInterval:(NSTimeInterval)intervalMs
104{
105  [self.manager setDeviceMotionUpdateInterval:intervalMs];
106}
107
108- (void)sensorModuleDidSubscribeForGyroscopeUpdates:(id)scopedSensorModule
109                                        withHandler:(void (^)(NSDictionary *event))handlerBlock
110{
111  if ([self.manager isGyroAvailable]) {
112    self.gyroscopeHandlers[((EXScopedEventEmitter *)scopedSensorModule).experienceId] = handlerBlock;
113  }
114  if (![self.manager isGyroActive]) {
115    [self.manager setGyroUpdateInterval:0.1f];
116    [self.manager startGyroUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMGyroData *data, NSError *error) {
117      for (void (^handler)(NSDictionary *) in self.gyroscopeHandlers.allValues) {
118        handler(@{
119                  @"x": [NSNumber numberWithDouble:data.rotationRate.x],
120                  @"y": [NSNumber numberWithDouble:data.rotationRate.y],
121                  @"z": [NSNumber numberWithDouble:data.rotationRate.z]
122                  });
123      }
124    }];
125  }
126}
127
128- (void)sensorModuleDidUnsubscribeForGyroscopeUpdates:(id)scopedSensorModule
129{
130  [self.gyroscopeHandlers removeObjectForKey:((EXScopedEventEmitter *)scopedSensorModule).experienceId];
131  if (self.gyroscopeHandlers.count == 0) {
132    [self.manager stopGyroUpdates];
133  }
134}
135
136- (void)setGyroscopeUpdateInterval:(NSTimeInterval)intervalMs
137{
138  [self.manager setGyroUpdateInterval:intervalMs];
139}
140
141- (void)sensorModuleDidSubscribeForMagnetometerUpdates:(id)scopedSensorModule
142                                           withHandler:(void (^)(NSDictionary *event))handlerBlock
143{
144  if ([self.manager isDeviceMotionAvailable]) {
145    self.magnetometerHandlers[((EXScopedEventEmitter *)scopedSensorModule).experienceId] = handlerBlock;
146  }
147  if (![self.manager isDeviceMotionActive]) {
148    [self activateDeviceMotionUpdates];
149  }
150}
151
152- (void)sensorModuleDidUnsubscribeForMagnetometerUpdates:(id)scopedSensorModule
153{
154  [self.magnetometerHandlers removeObjectForKey:((EXScopedEventEmitter *)scopedSensorModule).experienceId];
155  if (self.deviceMotionHandlers.count == 0 && self.magnetometerHandlers.count == 0) {
156    [self.manager stopDeviceMotionUpdates];
157  }
158}
159
160- (void)setMagnetometerUpdateInterval:(NSTimeInterval)intervalMs
161{
162  [self.manager setMagnetometerUpdateInterval:intervalMs];
163}
164
165- (void)sensorModuleDidSubscribeForMagnetometerUncalibratedUpdates:(id)scopedSensorModule
166                                                       withHandler:(void (^)(NSDictionary *event))handlerBlock
167{
168  if ([self.manager isMagnetometerAvailable]) {
169    self.magnetometerUncalibratedHandlers[((EXScopedEventEmitter *)scopedSensorModule).experienceId] = handlerBlock;
170  }
171  if (![self.manager isMagnetometerActive]) {
172    [self.manager setMagnetometerUpdateInterval:0.1f];
173    [self.manager startMagnetometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMMagnetometerData *data, NSError *error) {
174      for (void (^handler)(NSDictionary *) in self.magnetometerUncalibratedHandlers.allValues) {
175        handler(@{
176                  @"x": [NSNumber numberWithDouble:data.magneticField.x],
177                  @"y": [NSNumber numberWithDouble:data.magneticField.y],
178                  @"z": [NSNumber numberWithDouble:data.magneticField.z]
179                  });
180      }
181    }];
182  }
183}
184
185- (void)sensorModuleDidUnsubscribeForMagnetometerUncalibratedUpdates:(id)scopedSensorModule
186{
187  [self.magnetometerUncalibratedHandlers removeObjectForKey:((EXScopedEventEmitter *)scopedSensorModule).experienceId];
188  if (self.magnetometerUncalibratedHandlers.count == 0) {
189    [self.manager stopMagnetometerUpdates];
190  }
191}
192
193- (void)setMagnetometerUncalibratedUpdateInterval:(NSTimeInterval)intervalMs
194{
195  [self.manager setMagnetometerUpdateInterval:intervalMs];
196}
197
198- (void)activateDeviceMotionUpdates
199{
200  [self.manager setDeviceMotionUpdateInterval:0.1f];
201  [self.manager
202   startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical
203   toQueue:[NSOperationQueue mainQueue]
204   withHandler:^(CMDeviceMotion *data, NSError *error) {
205     UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
206     int orientationDegrees;
207     switch (orientation) {
208       case UIDeviceOrientationPortrait:
209         orientationDegrees = 0;
210         break;
211       case UIDeviceOrientationLandscapeLeft:
212         orientationDegrees = -90;
213         break;
214       case UIDeviceOrientationLandscapeRight:
215         orientationDegrees = 90;
216         break;
217       case UIDeviceOrientationPortraitUpsideDown:
218         orientationDegrees = 180;
219         break;
220       default:
221         orientationDegrees = 0;
222         break;
223     }
224
225     NSDictionary *result = @{
226                              @"acceleration": @{
227                                  @"x": @(data.userAcceleration.x * EXGravity),
228                                  @"y": @(data.userAcceleration.y * EXGravity),
229                                  @"z": @(data.userAcceleration.z * EXGravity)
230                                  },
231                              @"accelerationIncludingGravity": @{
232                                  @"x": @((data.userAcceleration.x + data.gravity.x) * EXGravity),
233                                  @"y": @((data.userAcceleration.y + data.gravity.y) * EXGravity),
234                                  @"z": @((data.userAcceleration.z + data.gravity.z) * EXGravity)
235                                  },
236                              @"rotation": @{
237                                  @"alpha": @(data.attitude.yaw),
238                                  @"beta": @(data.attitude.pitch),
239                                  @"gamma": @(data.attitude.roll),
240                                  },
241                              @"rotationRate" :@{
242                                  @"alpha": @(data.rotationRate.z),
243                                  @"beta": @(data.rotationRate.y),
244                                  @"gamma": @(data.rotationRate.x)
245                                  },
246                              @"orientation": @(orientationDegrees)
247                              };
248
249     // DeviceMotionUpdates handle DeviceMotion data as well as magnetic field
250     for (void (^handler)(NSDictionary *) in self.deviceMotionHandlers.allValues) {
251       handler(result);
252     }
253
254     for (void (^handler)(NSDictionary *) in self.magnetometerHandlers.allValues) {
255       handler(@{
256                 @"x": [NSNumber numberWithDouble:data.magneticField.field.x],
257                 @"y": [NSNumber numberWithDouble:data.magneticField.field.y],
258                 @"z": [NSNumber numberWithDouble:data.magneticField.field.z]
259                 });
260     }
261   }];
262}
263
264@end
265