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